Chickynoid, server authoritative character replacement

I think I am no longer an idiot and did it right this time! :smile:

wrote this simple mod which basicly allows you to add data to a table which then is sent as commands, the reason for this is because I want any script to at any point be able to create commands and be able to also remove commands

For example I have an OOP tool/inventory system, I make a class for it called melee, as soon as the equip function for my system is called on the client, we create the actions for the things we can perform (Block,Charge,Hit,Shove,Inspect) which starts sending commands to the server with the states of said actions. When we are done using our melee and we unequip it we can get rid of these actions so we stop wasting bandwith sending useless data to the server.

I guess ill test how this thing i made works and try to use the bitbuffer module for data that cannot be predicted, as for the input data to the server ill use something similar like enums just how MrChickenRocket did it that way the server already knows what the numbers sent mean.

3 Likes

Working on something big for chickynoid that I plan to release to the public which will facilitate a lot the developement for games on chickynoid.

Its Custom Tools for chickynoid, Chickynoid does not support roblox tools for obvious reasons, so I am writing a mod called InventoryMod, which allows chickynoids to have custom inventories that can hold different objects, It also already comes bundled with an object class called CTool (CitrusTool).

Citrus Tools imitate as much as possible the behavior of roblox tools via chickynoid and are split into different components to make its modification as easy as possible so you can adjust them to work into your game, I also plan to make them work instead of via input events via using a :ProcessCommand() function locally and on ServerSide so they work similar to the already existing weapons mod, and they also support rollback functions the user can code in, In case whatever you are coding desyncs from the server or needs to be controlled from the server, Such as unequipping an users tool.

Heres the way the Client sided components are built, keep in mind they are a rewrite of an older system I made so rn im just kinda rebuilding it and then modifying to work with chickynoid, Networking has not been done yet because I need to add a lot of things.

image

ClientInventoryMod

local module = {}
module.Objects = {}
module.InventoryRecords = {}

function module:Setup(client)
	-- Get all of our different Objects
	for i,v in pairs(script:GetChildren()) do
		if v:IsA("ModuleScript") then
			-- Setup and add our objects to the module's table
			local Contents = require(v)
			Contents:Setup()
			self.Objects[v.Name] = v
		end
	end
end

function module:Step(client, _deltaTime)
end

return module

CTool

-- Custom tools (CTool) mod!
local CTool = {}
-- Services
local Players = game:GetService("Players")
local StarterGui = game:GetService("StarterGui")
local ReplicatedFirst = game:GetService("ReplicatedFirst")

local LocalPlayer = Players.LocalPlayer
local FastSignal = require(ReplicatedFirst.Packages.Chickynoid.Vendor.FastSignal)

CTool.__index = CTool

CTool.ClassName = "CTool"
CTool.Classes = {}
CTool.IsSetup = false
CTool.ToolIDCounter = 0
CTool.InventoryRecords = {}
CTool.InventoryRecords.Server = {}
-- Events
CTool.OnCToolOwnershipToLocalPlayer = FastSignal.new()
CTool.OnCToolRemovedFromLocalPlayer = FastSignal.new()
CTool.OnCToolEquipped = FastSignal.new()
CTool.OnCToolUnequipped = FastSignal.new()
-- Client Only thing
CTool.InputHandler = nil

-- Constructor function for CTool Object
function CTool.new()
	local self = setmetatable({
		ID = CTool.ToolIDCounter,
		Owner = nil,
		IsEquipped = false,
		ClassName = CTool.ClassName,
		Origin = "Client",
		-- Change this when making a new class
		CToolClass = CTool.ClassName,
	}, CTool)
	-- Store our Object inside the Records so we can find it
	CTool.InventoryRecords[self.ID] = self
	-- Update the ID counter
	CTool.InventoryRecords += 1
	
	return self
end
-- Sets a CTool's Owner to the given Player
function CTool:SetOwner(Player)
	
	if CTool.InventoryRecords[Player.UserId] then
		self.Owner = Player
		CTool.InventoryRecords[Player.UserId].Backpack[self.ID] = self
		-- Run exclusive code to the Local Player
		if Player == LocalPlayer then
			CTool.OnCToolOwnershipToLocalPlayer:Fire(self)
		end
	else
		warn("Inventory not found")
	end
end
-- Unequips tool and runs class code
function CTool:Unequip()
	if self.IsEquipped == true then
		local Inventory = CTool.InventoryRecord[self.Owner.UserId]
		local ClassFunctions = require(script[self.ClassName])
		-- run it's class code
		self:UnequipClass()
		self.IsEquipped = false
		Inventory.Hand = nil
		-- Fire the local events for the input handler
		CTool.OnCToolUnequipped:Fire(self)
	end
end
-- Equip tool and runs class code
function CTool:Equip()

	if self.IsEquipped == false then

		local Inventory = CTool.InventoryRecord[self.Owner.UserId]

		local ClassFunctions = require(script[self.ClassName])
		-- Check if tool is already equipped and unequip if so
		if Inventory.Hand then
			Inventory.Hand:Unequip()
		end
		self:EquipClass()
		self.IsEquipped = true
		Inventory.Hand = self
		-- Fire the local events for the input handler
		CTool.OnCToolEquipped:Fire(self)
	end
end
-- Removes all traces of CTool object
function CTool:Destroy()
	-- Check if it has an owner to remove it from backpack and hand
	if self.Owner then
		local Inventory = CTool.InventoryRecords[self.Owner.UserId]

		if self.IsEquipped == true then
			self:Unequip()
		end
		
		Inventory.Backpack[self.ID] = nil
	end
	-- Fire local events for the input handler
	CTool.OnCToolRemovedFromLocalPlayer:Fire(self)
	self = nil
	return self
end
-- Sets up all the necesary code to make the object class fully work
function CTool:Setup()
	print("RAN")
	if CTool.IsSetup == false then
		-- Hide roblox's deffault inventory Gui
		StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false)
		
		-- Setup all other Classes of CTool
		for i,v in pairs(script:GetChildren()) do
			if v:IsA("ModuleScript") then
				local Class = require(v)
				Class:Setup()
				-- Detect if this is the Module in charge of controlling CTools, if it is then its not a class
				if Class.InputHandler then
					CTool.InputHandler = Class
				else
					print(v.Name)
					CTool.Classes[Class.ClassName] = Class
				end
			end
		end
	end
end

return CTool

InputHandler component

local InputHandler = {}
-- Services
local ContextActionService = game:GetService("ContextActionService")
local Players = game:GetService("Players")

local CTool = require(script.Parent)

local LocalPlayer = Players.LocalPlayer
InputHandler.InputHandler = true
-- idk load things
InputHandler.Assets = script.Assets

InputHandler.Hotbar = {}
InputHandler.HotbarSlotsInUse = 0
-- Settings
InputHandler.Settings = {}
InputHandler.Settings.Keybinds = {
	[1] = Enum.KeyCode.One,
	[2] = Enum.KeyCode.Two,
	[3] = Enum.KeyCode.Three,
	[4] = Enum.KeyCode.Four,
	[5] = Enum.KeyCode.Five,
	[6] = Enum.KeyCode.Six,
	[7] = Enum.KeyCode.Seven,
	[8] = Enum.KeyCode.Eight,
	[9] = Enum.KeyCode.Nine,
	[10] = Enum.KeyCode.Zero
}
InputHandler.Settings.MaxHotbarSlots = 10 -- This value should not be higher than 10

InputHandler.GUI = nil
-- Setup everything so we can start controlling the CTools
function InputHandler:Setup()
	-- Load the Gui
	self.GUI = self.Assets.ToolBar:Clone()
	self.GUI.Parent = LocalPlayer.PlayerGui
	-- Create the function in charge of handling all of our keypresses
	local function ActionHandler(Action,InputState,InputObject)
		if InputState == Enum.UserInputState.Begin then
			local ID = tonumber(Action)
			local CToolSelected = CTool.InventoryRecords[LocalPlayer.UserId].Backpack[ID]

			if CToolSelected.IsEquipped == true then
				CToolSelected:Unequip()
			else
				CToolSelected:Equip()
			end

		end
	end
	-- Connect events
	CTool.OnCToolOwnershipToLocalPlayer:Connect(function(OwnedCTool)
		-- Create our GUI for the Toolbar
		local NewToolFrame = self.GUI.ToolFrame:Clone()
		NewToolFrame.Name = OwnedCTool.ID
		NewToolFrame.NameLabel.Text = OwnedCTool.Name
		NewToolFrame.Visible = true
		-- Set up CTool inside a local hotbar slot
		if self.HotbarSlotsInUse <= self.Settings.MaxHotbarSlots then
			self.HotbarSlotsInUse += 1
			NewToolFrame.Parent = self.GUI.Hotbar
			
			for Slot = 1, self.MaxHotbarSlots,1 do
				if not self.Hotbar[Slot] then
					self.Hotbar[Slot] = OwnedCTool.ID
					NewToolFrame.NumberLabel.Text = Slot
					
					ContextActionService:BindAction(OwnedCTool.ID, ActionHandler,false, self.Keybinds[Slot])
					break
				end
			end
		else
			NewToolFrame.Parent = self.GUI.InventoryMenu
		end
	end)
	CTool.OnCToolRemovedFromLocalPlayer:Connect(function(CToolID)
		-- Removes the GUI Frame for the CTool, from the hotbar/Toolbar GUI
		self.GUI.HotBar[tostring(CToolID)]:Destroy()
		-- Clears the hotbar slot which references the CTool ID
		for Slot = 1, self.Settings.MaxHotBarSlots,1 do
			if self.HotBar[Slot] then
				ContextActionService:UnbindAction(CToolID)
				self.HotBar[Slot] = nil
			end
		end
	end)
	CTool.OnCToolEquipped:Connect(function(EquippedCTool)
		-- Show the GUI effects
		self.GUI.HotBar[EquippedCTool.ID].Outline.Visible = true
	end)
	CTool.OnCToolUnequipped:Connect(function(EquippedCTool)
		-- Show the GUI effects
		self.GUI.HotBar[EquippedCTool.ID].Outline.Visible = false
	end)
end

return InputHandler

Ill publish it as a resource once its fully ready, Let me know what you guys think!

7 Likes

Very cool, but I don’t see this becoming the next best character replacement due to the fact there’s a ton, I mean a ton of bug’s in the system not to mention this in its self is a bad idea,

You’re making two objective claims without explanation or evidence:

What kind of deal-breaking bugs are there in the system?

Why is a server-authoritative character controller a bad idea? I think it’s a really clever solution.

Currently, in most games, the player’s character is owned by the client. It’s an easy solution, but very exploitable.

The idea of Chickynoid is to let the server own the character. By using deterministic movement code, the client can provide an input and run the outcome before the server can respond and come to the same solution. So, input lag is minimal and the security is bulletproof.

Of course, it requires a lot of development since the entire character controller is being written from scratch. But once this is production-ready or if Roblox takes attention, character-based exploits are practically ineffective.

tl;dr: Chickynoid is not an anti-exploit. It does not respond reactively to abuse at the edge. Instead, it solves the problem at the root.

13 Likes

I have been using it for developement and have not found any issues that are game breaking or anything, the maximum I found was the fast delta time exploit to slightly give players a higher jump boost (its like very minimal).

Also saying this is a bad idea is a bit silly considering all triple A titles out there you see use this solution already.

4 Likes

The mod has been released as an alpha, and I think its on a semi stable state, and also utilizes chickynoid features and works with mods!

Make sure to check it out if you are interested in learning about writing mods for chickynoid or wanna learn how to make simple networking with it, or you are just looking for an inventory system for chickynoid.

Chickytools [alpha V1.0.3],A tool based inventory system for chickynoid! - Resources / Community Resources - DevForum | Roblox

1 Like

does it work for R6? because I only found R15 examples

Yes it does!

image

Get an R6 rig and name it to “R15Rig” then simply use it to replace the one located on here
image

Then go into the CharacterModel module script and make sure to have the hip set to this or your preference for your R6 rig to not be clipping into the ground
image

Make sure to put all your deffault animations inside the Rig’s humanoid’s animator as animation instances with these names
image

Simple as that!

8 Likes

Thank you for writing that up! You should submit it as a documentation PR :smiley:

3 Likes

Is this still under development? The last update to the GitHub was 4 months ago.

It’s in a stable state. Games are shipping with it atm. It’ll get some updates when new roblox tech comes along like unreliable remotes :slight_smile:

4 Likes

Can you post a few games that use it? Would work good as an example to show how well it works!

1 Like

Here is a prototype of a game built on chickynoid: (10) project rails prototype - Roblox

Its simple but cool

1 Like

@MrChickenRocket
I have a question, should we use commands for anything related to sending data between clients and server or should we use a mix of commands and remote events, I am asking this because I am not sure if it’s very effective sending data every frame for some things.

If you’re worried about how much data is in commands it should be possible to use table delta compression on the commands. It’s already used in a few other places.

If the input needs to be predicted, use a command, else use whatever you like.

1 Like

@MrChickenRocket I got an error saying this when I inserted Chickynoid inside
the game Flee the Mall

ReplicatedFirst.Packages.Chickynoid.Simulation.CollisionModule:892: ReplicatedFirst.Packages.Chickynoid.Simulation.CollisionModule:847: attempt to index nil with 'GetDescendants'

1 Like

did you just copied the chickynoid module and inserted it to your game, because if so then its not gonna work, You need to insert the module, the server mods, and also you must have a script that sets up the necesary mods such as generate command and the other one i forgot its name.

Uhh yes I did also idk where to put the scripts in github

i recommend you to download the roblox place file and grab everything from there.

I have the file and I added everything from the uncopylocked place