pickleplayer Posted March 15, 2017 Share Posted March 15, 2017 Heyy, it's the smashup guy here. Last week I decided to throw the code I have into DST just to see what I could do with it, but I am quickly remembering just how little I actually know about this version of Don't Starve. I've spent the better half of this week studying it and I just... don't get it. I can't get the client to interact with the host the way I want it to. The client can barely do anything. I know that everything for the client needs to happen before all that pristine and ismastersim stuff happens, but I still can't get it to work correctly. I can get the client's stategraph states to work when called from certain places, but not from key handlers initiated before pristine. So I started again with baby steps to figure out whats wrong. Like, the babiest of baby steps. I made a keydownhandler in the modmain to make a button that spawns nitre and teleports it to any player's location. It didn't work. It shows up fine when used by the host, but for the client, the nitre only appears on their screen. Why is that? I'm honestly not sure where else to go from here, I can't think of any smaller steps to take. I guess I should just start with the basics and just ask "how do I spawn a prefab as a client?" Link to comment Share on other sites More sharing options...
PanAzej Posted March 16, 2017 Share Posted March 16, 2017 (edited) 5 hours ago, pickleplayer said: how do I spawn a prefab as a client? You don't. Clients have limited interaction, you can't just spawn a prefab only on client's side. If you want everything to be usable/visible for all players, then make it serverside. Here's what happens for the client when you spawn an item: local inst = CreateEntity() inst.entity:AddTransform() inst.entity:AddAnimState() inst.entity:AddSoundEmitter() inst.entity:AddNetwork() inst.AnimState:SetBank("bank") inst.AnimState:SetBuild("build") inst.AnimState:PlayAnimation("anim") inst.entity:SetPristine() if not TheWorld.ismastersim then return inst end And that's it. You can see the item, but TheWorld is not master on client, and that's where the stuff ends for him. The rest is handled serverside, but since client spawned it, the item "exists" only for him. Clients don't have components (well, there's a few exceptions), so they can't initiate them. That's why the item doesn't work at all. Most (if not all) functions, etc. should run serverside. Edited March 16, 2017 by PanAzej Link to comment Share on other sites More sharing options...
pickleplayer Posted March 16, 2017 Author Share Posted March 16, 2017 (edited) 14 hours ago, PanAzej said: You don't. Clients have limited interaction, you can't just spawn a prefab only on client's side. If you want everything to be usable/visible for all players, then make it serverside. Here's what happens for the client when you spawn an item And that's it. You can see the item, but TheWorld is not master on client, and that's where the stuff ends for him. The rest is handled serverside, but since client spawned it, the item "exists" only for him. Clients don't have components (well, there's a few exceptions), so they can't initiate them. That's why the item doesn't work at all. Most (if not all) functions, etc. should run serverside. Ohhh, alright. So any prefabs spawned by clients will just not exist for anyone but themselves. But what about Wes's balloons? The balloon prefabs don't look much different from any other basic prefab's code. And the balloonmaker component doesn't do anything more than spawn the balloon prefab and teleport it to his location. So what makes it different? Why does that one show up for host just fine? Does it have anything to do with the "balloonmancer" tag that is added onto Wes before the pristine sets in? I have a component that sets in before pristine that adds all of the keydown handlers that push the events the custom stategraph needs to function, and it still doesn't show up for host. Is that not how you add a component server side? Edited March 16, 2017 by pickleplayer Link to comment Share on other sites More sharing options...
CarlZalph Posted March 16, 2017 Share Posted March 16, 2017 43 minutes ago, pickleplayer said: Ohhh, alright. So any prefabs spawned by clients will just not exist for anyone but themselves. But what about Wes's balloons? The balloon prefabs don't look much different from any other basic prefab's code. And the balloonmaker component doesn't do anything more than spawn the balloon prefab and teleport it to his location. So what makes it different? Why does that one show up for host just fine? Does it have anything to do with the "balloonmancer" tag that is added onto Wes before the pristine sets in? I have a component that sets in before pristine that adds all of the keydown handlers that push the events the custom stategraph needs to function, and it still doesn't show up for host. Is that not how you add a component server side? It has an action associated with it, and an RPC. The action lets the user right click the item and the RPC is sent in the action callback to let the server know the client's intention of making a balloon. Link to comment Share on other sites More sharing options...
PanAzej Posted March 16, 2017 Share Posted March 16, 2017 57 minutes ago, pickleplayer said: But what about Wes's balloons? The balloon prefabs don't look much different from any other basic prefab's code. And the balloonmaker component doesn't do anything more than spawn the balloon prefab and teleport it to his location. So what makes it different? Why does that one show up for host just fine? Does it have anything to do with the "balloonmancer" tag that is added onto Wes before the pristine sets in? Here's step-by-step what happens with Wes's balloons: 1. ComponentAction: INVENTORY = --args: inst, doer, actions, right { balloonmaker = function(inst, doer, actions) if doer:HasTag("balloonomancer") then table.insert(actions, ACTIONS.MAKEBALLOON) end end } That's what this "balloonomancer" tag is for in Wes's prefab. Componentaction checks are run on clients, and since clients don't have components, you have to rely on either tags or replicable components. This thing here makes it so we see the prompt for "Make Balloon" action. 2. Actionhandler: ActionHandler(ACTIONS.MAKEBALLOON, "makeballoon") When we click - we're sent to the stategraph state through this ActionHandler. 3. Stategraph State: State{ name = "makeballoon", tags = { "doing", "busy", "nodangle" }, onenter = function(inst, timeout) inst.sg.statemem.action = inst.bufferedaction inst.sg:SetTimeout(timeout or 1) inst.components.locomotor:Stop() inst.SoundEmitter:PlaySound("dontstarve/common/balloon_make", "make") inst.SoundEmitter:PlaySound("dontstarve/common/balloon_blowup") inst.AnimState:PlayAnimation("build_pre") inst.AnimState:PushAnimation("build_loop", true) end, timeline = { TimeEvent(4 * FRAMES, function(inst) inst.sg:RemoveStateTag("busy") end), }, ontimeout = function(inst) inst.SoundEmitter:KillSound("make") inst.AnimState:PlayAnimation("build_pst") inst:PerformBufferedAction() end, events = { EventHandler("animqueueover", function(inst) if inst.AnimState:AnimDone() then inst.sg:GoToState("idle") end end), }, onexit = function(inst) inst.SoundEmitter:KillSound("make") if inst.bufferedaction == inst.sg.statemem.action then inst:ClearBufferedAction() end end, }, inst:PerformBufferedAction() is where we're sent to... well, perform our buffered action. 4. Action. ACTIONS.MAKEBALLOON.fn = function(act) if act.doer ~= nil and act.invobject ~= nil and act.invobject.components.balloonmaker ~= nil and act.doer:HasTag("balloonomancer") then if act.doer.components.sanity ~= nil then if act.doer.components.sanity.current < TUNING.SANITY_TINY then return false end act.doer.components.sanity:DoDelta(-TUNING.SANITY_TINY) end --Spawn it to either side of doer's current facing with some variance local x, y, z = act.doer.Transform:GetWorldPosition() local angle = act.doer.Transform:GetRotation() local angle_offset = GetRandomMinMax(-10, 10) angle_offset = angle_offset + (angle_offset < 0 and -65 or 65) angle = (angle + angle_offset) * DEGREES act.invobject.components.balloonmaker:MakeBalloon( x + .5 * math.cos(angle), 0, z - .5 * math.sin(angle) ) return true end end Action itself is executed serverside. And that's the end here. --- I haven't toyed around with KeyHandlers, though I'd say you should look how these are done in mods like Playable Pets or Puppy Princess. Link to comment Share on other sites More sharing options...
CarlZalph Posted March 16, 2017 Share Posted March 16, 2017 @PanAzej Forgot the RPC that actually networks it to the server. In this case right-clicking on the stack of balloons calls RPC.UseItemFromInvTile with the ACTIONS.MAKEBALLOON.code. RPC.UseItemFromInvTile calls inventory:UseItemFromInvTile which eventually gets to a buffered action. Link to comment Share on other sites More sharing options...
pickleplayer Posted March 17, 2017 Author Share Posted March 17, 2017 Alright! Thank you for the explanation about actions and RPCs and links to mods. I've been going through it all and I'm still figuring out RPCs, but I managed to get a key handler event to work between client and host with that new knowledge, so that's good. So I know how to get the client to send events to the host, but now I'm wondering if there's any way for the host to detect if the client is holding a button down. I'll have to look into that at some point. Guess I'll be spending a while reworking the control scheme until I run into any other problems. Thanks! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now