Chickynoid, server authoritative character replacement

Bots and players both use playerRecords. From what I can tell, I think there is a race condition between setting the bot’s position and waiting for it to hit the ground (like in your screenshot) and the server waiting for the first command from playerRecord.BotThink.

In other words, your bot is too slow to start telling the server what it wants to do before it sinks into the ground.

I think this could be avoided if, before sending any commands through .BotThink, you send a command to the server upon spawning it in.

Somewhere between setting the position of the bot and defining .BotThink, try constructing a command,

local command = {}
command.localFrame = playerRecord.frame
command.playerStateFrame = 0
command.x = 0
command.y = 0
command.z = 0
command.serverTime = tick()
command.deltaTime = deltaTime

encoding it,

local event = {}
event[1] = CommandLayout:EncodeCommand(command)

and telling the chickynoid to handle the event.

playerRecord.chickynoid:HandleEvent(Server, event)

The example Bots module given in the example place works because playerRecord.waitTime is 0 when .BotThink is first called, so as soon as it’s good to go, the bot sends a command with x, y, z = 0.

If this is still an issue for you, I can try to look further into the source code to determine why the bot’s chickynoid collision hull is sinking into the ground. I can’t remember right now, but I think the server doesn’t take the bounds of the collision hull into account until the chickynoid is loaded after receiving its first event. I think it was to prevent players from falling through the world before the client can send in their initial load event. E.g. let the player flail in the ground until the server recognizes their input commands, similar to your current situation.

2 Likes

This seemed to be the issue. Thanks.

1 Like

To clarify, you can put a cap on the number of commands the server will listen to.


Roblox also caps the amount of kb per second the server’s can receive, so the only way to DDOS the server is via external tools.

Thank you for finding that setting. I recently commented the entire codebase and couldn’t remember if there was a cap on the server as well.

For anyone else looking for how to change the command rate cap for the server, you’ll find it under ServerScriptService > Packages > Chickynoid > Server > ServerChickynoid.

You’re right though, DDOS was not the right word. I did say “almost like” because a server can’t receive as many packets as it gets sent. I wanted to draw a parallel because OP (znimator) asked -

Why I need that and what happens if other players just Ignore that warning.

1 Like

How do you change the player’s collision box’s hull to a sphere?

In the ServerChickynoid Module at 458 there is a function,

function ServerChickynoid:UpdateServerCollisionBox(server)
Change the box to a sphere by changing its shape.

 if self.hitBox == nil then
        --This box is also used to stop physics props from intersecting the player. Doesn't always work!
		--But if a player does get stuck, they should just be able to move away from it
		local box = Instance.new("Part")
		box.Shape = Enum.PartType.Ball
		
		box.Size = Vector3.new(3,5,3)
		box.CanCollide = false
		box.CanQuery = false
		box.CanTouch = false
		box.Anchored = true
		box.Parent = server.worldRoot
        box.Position = self.simulation.state.pos
        box:SetAttribute("player", self.playerRecord.userId)
        self.hitBox = box
        self.hitBoxCreated:Fire(self.hitBox);

        --for streaming enabled games...
        if self.playerRecord.player then
            self.playerRecord.player.ReplicationFocus = self.hitBox
        end
    end
		

image

1 Like

If anyone is willing to help or provide insight, I’d like to implement a new feature to dynamically adjust a chickynoid’s size in game.

From my understanding, Chickynoid uses a hard coded size vector across some of its modules. To note, I’ve found this inside of:

  • ServerModule
    ServerModule.playerSize = Vector3.new(2, 5, 2)

  • ServerChickynoid
    box.Size = Vector3.new(3, 5, 3)

  • CollisionModule
    CollisionModule.expansionSize = Vector3.new(2, 5, 2)

  • ClientModule
    ClientModule.playerSize = Vector3.new(2,5,5)

Interesting enough, ClientModule doesn’t actually use (2, 5, 5). It sets the player size when the (unreliable) remote calls the function eventHandler[EventType.CollisionData]. It then goes on to simulate its own world for their client’s chickynoid.

ServerChickynoid:UpdateServerCollisionBox(server) seems to mainly be responsible in querying if a chickynoid is hit by a bullet’s raycast. You’ll notice the collision box has the attribute "player". In WeaponsServer, this attribute is checked to determine if a part (the collision box) belongs to a player.

The first step, I’d assume, is to unify this value across the modules by using the playerRecord.playerSize’s property.

Everything should still initialize as normal, however, I’ve noticed a big problem. While the client simulates their own collision world for their chickynoid, the server also simulates one, but uses the same one for every chickynoid.

ServerModule:RecreateCollisions(workspace:FindFirstChild("GameArea")) eventually leads to CollisionModule:MakeWorld(folder, playerSize).

Looking into CollisionModule, a world is a bloated version of whatever root folder you give it, e.g. all parts (and terrain) are “expanded” by the playerSize. This is done because chickynoid collisions are done via point vs. hull intersections. You can see this any time CollisionModule:Sweep() is called inside of Simulation.

It’s not as simple as changing the playerSize parameter, however, as any new world created with it overwrites the old one.

I have two ideas on what I can do from here.

  1. Create and reference a collision world for each chickynoid’s simulation. This can be updated when the chickynoid’s playerRecord is updated, destroyed when the record is destroyed, etc.
  2. Rework collisions to consider hull vs. hull intersections, e.g. shapecasts, instead and forego creating worlds for each chickynoid.

I can’t imagine the server being able to handle a separate collision world for each chickynoid. Each world needs its own spatial partitioning grid, each part would need its hulls regenerated any time the playerSize updates, and memory usage scales linearly with chickynoid count (unless I made same sized chickynoids share a world).

Implementing shapecasting is its own pandora’s box. I’ve done it before when shapecasting wasn’t a thing but the biggest issue was preventing misses. That’s a whole separate (skill) issue.

I believe the fact that shapecasts don’t have margins is the reason why point vs. hull intersection collision is still being used in Chickynoid.

I’m just stumped as to what to do since the game I’d like to make with Chickynoid would have characters with different sizes, otherwise I’d just make a different game.

3 Likes

Afaik all player boxes are the same size in the custom physics engine for performance reasons or something, not sure.

I say we just wait for margins, it might prob take a long while, but by the time we finish making a billion hacks to the custom physics engine, shapecasting with margins will prob already have been released.

Yep, wait for margins!

If you absolutely must do it, you need to update the collision cast methods to take any sized box as a parameter, and key the box size to the cache for the minkowski expansions deeper in the routine.

Hi Mr Chicken! Here is a R6Rig I made with
the default animations inside the humanoid.
R6Rig.rbxm (9.6 KB)

Also this!

1 Like

That fun!
Also posts must be at least 29+1 characters.

1 Like

This was my concern before even attempting this. :sob:

I feel like it can be done but it wouldn’t be worth it in the long run.

1 Like

I see. When MinkowskiSumInstance:GetPlanesForInstance() is called, it could fetch an instance’s planes from a cache depending on whatever playerSize is used. If it doesn’t exist, it’s as simple as generating and caching one with the existing code.

Maybe you’d understand better, @MrChickenRocket, but is it worth it to implement variable chickynoid size? I haven’t tried it, but from intuition, I feel like the performance tradeoff isn’t worth it. I don’t know enough but I doubt making this in Luau on top of Roblox’s engine would come close to what Roblox themselves could do.

You got it, that’s the idea. Is it worth it? Well, if you need just a few sizes of character then probably its fine, its not a huge chunk of work.
Is it ever going to be competitive with native casts? No. But it’s still very, very fast.

1 Like

I managed to get no humanoid rigs working on roblox studio, I can finally ditch humanoids entirely on chickynoid!

They can load clothes through surface appearances.

Here is the rig if anyone wants it:
NoHumanoidR6Rig.rbxm (51.1 KB)

6 Likes

I needed that confirmation as permission to do it - you don’t know until you try!

and try I did.


(just pretend for now that the character would fill up the size of that red box)

Here’s with debugging hull expanded visuals enabled.

It did take some trial and error though. I cut a lot of unused bits of code out from the CollisionModule to make it easier to work on. The unexpected twist was figuring out how the pattern for returning hulls from terrain cells was different from returning hulls from instances in workspace. I had to modify TerrainModule:FetchCell() to include expansionSize (aka playerSize) in its parameters.

For a short explanation, the hulls for instances, meshparts, and terrain exist in a spatial partitioning grid (implemented as a hash map). Instances and meshparts have a condition where they might exist in multiple cells of the grid and would be cached in the fatGrid. Depending on the position that :Sweep is called, the CollisionModule finds the appropriate cell and returns all hulls within that cell. From there, its as simple as multiplying the points of each hull by an expansionSize (Vector3 * number) and caching it.

Instances:

MeshParts:

Terrain:
image

For networking, I had to change the :SendCollisionData() parameters to include playerSize as well instantiate CharacterData with it. Big shoutout to @CCTVStudios for the mini-tutorial on how to work with the CharacterData module.

image
image
image

Most of the time was spent understanding what was really going on (as well as restoring code I shouldn’t have deleted :crazy_face:). It looks simple but there’s a lot going on that Chickynoid already handles.

That’s as far as I got today. I’ll return to this in a few days when I get the time. I still have to implement a way to update playerSize and clean up unused memory.

2 Likes

I’m glad you found my tutorial from like a year ago useful.

1 Like

I had no idea you moved to a different account! I would lurk this thread when I first discovered Chickynoid (about a year or two ago). You and amp’s work inspired me to pull the trigger late this year and work with the library.

The biggest hurdle is simply lack of documentation. I don’t have a lot of free time, so I try to get as much out of Roblox development when I can. Posts like yours make it easy to know what to look for to change something.

I don’t want it to go unappreciated in a project like this, so thank you again :smile:

1 Like

The chickynoid humanoid less rig is now out for the public!

Citrus R6 - R6 rigs without humanoids - Resources / Community Resources - Developer Forum | Roblox

Finally! get rid of humanoids from chickynoid once in for all!

1 Like