Chickynoid, server authoritative character replacement

This seemed to fix the issue, thanks.

Had no clue the client side Chickynoid models still had the ability to push objects. The default Roblox physics still messes with my head today.

2 Likes

I still haven’t figured this out.

Is there anyone here who knows a little bit about the CollisionModule and TerrainCollision modules?

I imagine I’d be able to make this change myself if I knew a bit more about the actual code for collisions.

I confirm that this is accurate information.

1 Like

I figured this out.

I turned off water collision by changing the Sample() function inside the TerrainCollision module.

local water = Enum.Material.Water

local function getAverage(occs, mats, x, y, z)	
	local val = occs[x][y][z]
	
	if mats[x][y][z] == water then
		return -(val*0.175)
	end
	
	return val
end

local function Sample(occs, mats, x, y, z)	
	local avg = getAverage(occs,mats,x+0,y+0,z+0)	
	avg += getAverage(occs,mats,x+1,y+0,z+0)
	avg += getAverage(occs,mats,x+0,y+0,z+1)
	avg += getAverage(occs,mats,x+1,y+0,z+1)
	avg += getAverage(occs,mats,x+0,y+1,z+0)
	avg += getAverage(occs,mats,x+1,y+1,z+0)
	avg += getAverage(occs,mats,x+0,y+1,z+1)
	avg += getAverage(occs,mats,x+1,y+1,z+1)

	avg /= 8
	
	avg = math.floor(avg * terrainQuantization) / terrainQuantization
    return avg
end

There’s probably a better way of doing this, but this solution seems to work well for normal uses.

You get the “mats” variable by passing the “materials” variable from the :ReadVoxels function

 local materials, occs = game.Workspace.Terrain:ReadVoxels(region, 4)
local topA = Sample(occs, materials, xd + 0, yd + 1, zd + 0)
local topB = Sample(occs, materials, xd + 1, yd + 1, zd + 0)
local topC = Sample(occs, materials, xd + 0, yd + 1, zd + 1)
local topD = Sample(occs, materials, xd + 1, yd + 1, zd + 1)
local botA = Sample(occs, materials, xd + 0, yd + 0, zd + 0)
local botB = Sample(occs, materials, xd + 1, yd + 0, zd + 0)
local botC = Sample(occs, materials, xd + 0, yd + 0, zd + 1)
local botD = Sample(occs, materials, xd + 1, yd + 0, zd + 1)

I didn’t even know water had collisions on chickynoid, what the flip.

Hey @MrChickenRocket, is Chickynoid going to be obsolete in 2025?

4 Likes

I’ve been messing around with this and I added a few move states. I’m running into an issue where unreliable remotes are being dropped for being oversized. Specifically when I add to the simulation.constants table it starts dropping remotes. Am I doing something wrong and misusing the table or do I just need to keep the constant count low? Any help would be appreciated, Thanks.

Its probably because simulation.constants is larger than the 900 byte limit that unreliable remotes have causing your packets to get dropped.

I was thinking that, but I was under the impression it was delta compressed so as long as they don’t change it wouldn’t be going over the limit. Also It seems a bit odd to be replicating something that should be constant right?

1 Like

Yeah so from what I understood the compression on chickynoid is useless because it will only compress if the packet is under 900 bytes. I could be wrong.

1 Like

Would you suggest I just put the constants in their own module script and separate it out from the simulation object?

1 Like

How would I implement some sort of timer for a knockback system? I tried using command.serverTime but the value seems to be stuck at 0 for bots

1 Like

I just have it send the initial state through a reliable remote, it only took me adding 5 lines. Is there a reason I shouldn’t be doing this? I don’t understand why this wasn’t already a thing.

I have rewritten the collision module to use block casting; after lots of tweaking, I’ve gotten it to a state where I can no longer clip into objects, and there are no mispredicts! It is pretty simple, but it took me forever to think of it. Now, we can use variable-sized hitboxes!

This would probably get better results with capsule colliders, but its been working fine for me with the block ones.

It is a drop in replacement, the only bit of code that needs changing is the ProjectVelocity function in the Simulation module.

        if planes[result.normal] == nil then
			planes[result.normal] = true

            --Deflect the velocity and keep going
            moveVel = MathUtils:ClipVelocity(moveVel, result.normal, 1.0)
        else
            --We hit the same plane twice, push off it a bit
            movePos += result.normal * 0.01
            moveVel += result.normal
            break
        end

I changed it to use the results normal instead of its plane number.

local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local Debris = game:GetService("Debris")
local PhysicsService = game:GetService("PhysicsService")

if RunService:IsServer() then -- probably just create these in studio beforehand or the server module instead of here
	PhysicsService:RegisterCollisionGroup("GameArea")
	PhysicsService:CollisionGroupSetCollidable("GameArea","GameArea",true)
	PhysicsService:CollisionGroupSetCollidable("GameArea","Default",false)
end


local module = {}

-- Constants
local DEFAULT_SIZE = Vector3.new(3, 5, 3)
local SHIFTBACK = 1 -- shift start position of the cast back by a constant offset, in opposite direction of movement. this will avoid the cast ever starting too close to a wall and us phasing through. There are some quirks when going over small terrain or features.
-- under 1 there are some rare cases when at extreme angles that you can get close enough to a wall to phase through it

function module:Sweep(startPos, endPos) -- not sure how to properly impelement all solid and start solid, possibly do a getpartBoundsInBox check at the starting position, but that will pick up meshparts incorrectly
	local data = {
		startPos = startPos,
		endPos = endPos,
		fraction = 1,
		startSolid = false,
		allSolid = false,
		normal = Vector3.new(0, 1, 0),
	}
	
	
	local offset = endPos-startPos
	local distance = offset.Magnitude
	
	if distance <= 0 or distance >= 1024 then
		return data
	end
	
	local direction = offset.Unit
	
	local params = RaycastParams.new()
	params.CollisionGroup = "GameArea"
	--params.FilterType = Enum.RaycastFilterType.Exclude
	--params.FilterDescendantsInstances = {}


	local castOrigin = startPos-(direction*SHIFTBACK)
	

	local result = workspace:Blockcast(
		CFrame.new(castOrigin),
		DEFAULT_SIZE,
		direction*(distance+SHIFTBACK),
		params
	)


	if result then

		data.fraction = (result.Distance-SHIFTBACK) / direction.Magnitude
		data.normal = result.Normal
		data.endPos = castOrigin + direction.Unit * result.Distance
		--data.allSolid = data.startSolid and data.fraction < 0.001
	else
		data.endPos = endPos
	end

	return data
end


function module:MakeWorld(folder)
	debug.setmemorycategory("Collision")

	-- Process existing parts
	for _, instance in ipairs(folder:GetDescendants()) do
		if instance:IsA("BasePart") and instance.CanCollide then
			instance.CollisionGroup = "GameArea"
		end
	end

	-- Handle new parts
	folder.DescendantAdded:Connect(function(instance)
		if instance:IsA("BasePart") and instance.CanCollide then
			instance.CollisionGroup = "GameArea"
		end
	end)

end


return module
2 Likes

As stated previously by MrChickenRocket, there are some mispredicts on rotated base parts due to how Roblox replicates them with slight inaccuracies. We can’t account for that in the collision detection now, so it may be necessary to round them on the client and server at runtime or manually replicate the accurate CFrames.

I don’t think this is for fighting games because I’m pretty sure it allows both clients to desync while still validating [ whatever clients must have seen ] on the server.

Assuming I’m correct, most games should be able to use this. However, for fighting games it’s not ideal to have any form of desync.

I found a pretty good article that shows the downsides of both netcodes

Netcode Architectures Part 3: Snapshot Interpolation | SnapNet
vs.
Netcode Architectures Part 2: Rollback | SnapNet

For those looking for a head start on client-side prediction rollback, you should be able to find helpful information in this thread.

2 Likes

Humanoids are giving me a receive rate of 200kbps on a 70 player server, would chickynoid fix this? What would receive rates per player look like? I assume the best solution for low receive rate would be client-sided physics characters, but that comes with cheats being easy to make.

Differences in Jump Height when you press vs hold the jump button

anybody know a fix?

It should be inside the walking move state, I modified mine a lot, so I don’t remember exactly where, but it should be in there.

This might be the issue