Jump to content

What Components do Clients Have?


Recommended Posts

Okay, so basically in my mod I've been trying to reference things like Health or even the Leader component, and it works for the server hoster, but not for those who connect.  Is there a way around this dilemma?  Is there to refer to Health or other components for clients?

Link to comment
Share on other sites

@Mario384,

 

Some components are client side, like playercontroller, playeractionpicker, playertargetindicator, and talker.

You can see this if you join a dedicated server and use:

for k, v in pairs(ThePlayer.components) do print(k, v) end

The components that get a replica are the ones in REPLICATABLE_COMPONENTS in entityreplica.lua.

You can extend this table via the mod api function AddReplicableComponent(name).

 

So, what are you specifically trying to do?

Depending on that, you can refer to a replica via inst.replica.health for example.

 

But you can't currently do, say, get client side, the health number a spider has.

Because since clients don't do anything with that number, Klei didn't make it leave the server.

 

Leader does not have a replica, so you will have to make a replica yourself.

Or make net variables elsewhere in the code.

 

But we need to know what we are dealing with here.

Link to comment
Share on other sites

@Mario384,

 

Some components are client side, like playercontroller, playeractionpicker, playertargetindicator, and talker.

You can see this if you join a dedicated server and use:

for k, v in pairs(ThePlayer.components) do print(k, v) end

The components that get a replica are the ones in REPLICATABLE_COMPONENTS in entityreplica.lua.

You can extend this table via the mod api function AddReplicableComponent(name).

 

So, what are you specifically trying to do?

Depending on that, you can refer to a replica via inst.replica.health for example.

 

But you can't currently do, say, get client side, the health number a spider has.

Because since clients don't do anything with that number, Klei didn't make it leave the server.

 

Leader does not have a replica, so you will have to make a replica yourself.

Or make net variables elsewhere in the code.

 

But we need to know what we are dealing with here.

local function OnKeyAbility(inst, data)    if data.inst == ThePlayer then        if data.key == KEY_X then     if TheWorld.ismastersim then    inst:RemoveTag("werepig")    inst:AddTag("guard")    inst:SetStateGraph("SGpigplayer")	inst.AnimState:SetBuild("pig_guard_build")        if inst.components.hauntable then         inst.components.hauntable.haunted = false         inst.components.hauntable.cooldown_on_successful_haunt = true    end    inst.components.combat:SetDefaultDamage(TUNING.PIG_GUARD_DAMAGE)    inst.components.combat:SetAttackPeriod(TUNING.PIG_GUARD_ATTACK_PERIOD)    inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED    inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED	inst.components.health:DoDelta(-40, nil, nil, nil, nil, true)        inst.components.lootdropper:SetLoot({})    inst.components.lootdropper:AddRandomLoot("meat",3)    inst.components.lootdropper:AddRandomLoot("pigskin",1)    inst.components.lootdropper.numrandomloot = 1    inst.components.trader:Enable()    inst.components.talker:StopIgnoringAll()    inst.components.follower:SetLeader(nil)    inst.components.combat:SetHurtSound(nil) else    inst:RemoveTag("werepig")    inst:AddTag("guard")    inst:SetBrain(guardbrain)    inst:SetStateGraph("SGpigplayer")	inst.AnimState:SetBuild("pig_guard_build")        if inst.components.hauntable then         inst.components.hauntable.haunted = false         inst.components.hauntable.cooldown_on_successful_haunt = true    end    inst.components.combat:SetDefaultDamage(TUNING.PIG_GUARD_DAMAGE)    inst.components.combat:SetAttackPeriod(TUNING.PIG_GUARD_ATTACK_PERIOD)    inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED    inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED        inst.components.lootdropper:SetLoot({})    inst.components.lootdropper:AddRandomLoot("meat",3)    inst.components.lootdropper:AddRandomLoot("pigskin",1)    inst.components.lootdropper.numrandomloot = 1    inst.components.trader:Enable()    inst.components.talker:StopIgnoringAll()    inst.components.follower:SetLeader(nil)    inst.components.combat:SetHurtSound(nil)            end        end    endend

Alright, here's a slather of code.  See, originally in the beginning it checked to see if the player's a health was above 41, and then would exocute it.  While this worked for the server hoster, it would crash clients.  It also crashed due to *inst.components.health:DoDelta(-40, nil, nil, nil, nil, true)*  being htere, so I had to set up this instead.  So, if I understand what you're saying, if I put *inst.replica.health:DoDelta(-40, nil, nil, nil, nil, true)* in my second part, it will function?  Ad if so, how can I check for one's current replica health without crashing the game?

Edited by Mario384
Link to comment
Share on other sites

@Mario384,

 

That triggers when the player presses any key, right?

But only activates with the KEY_X.

 

You don't need to run any replica changes or anything, you only need the client to trigger a code change in the server.

 

Imagine if one client could run code that modifies replicas and that info gets to the server.

Cheating galore, you wouldn't need a mod that allows that.

 

As such, what you do is put the server code inside a AddModRPCHandler.

And inside a OnKeyAbility, you put a SendModRPC, so the client tells the server to do something.

AddModRPCHandler(modname, "triggerchange", function(player)	local inst = player	-- code between "if TheWorld.ismastersim then" and "else" goes hereend)local function OnKeyAbility(inst, data)	if inst == GLOBAL.ThePlayer and data.key == GLOBAL.KEY_X then		SendModRPCToServer(MODRPC[modname]["triggerchange"])	endend

The OnKeyAbility will presumably be handled by a key event, yes?

 

Also, there's no such DoDelta function in health_replica, you can check what you can use from there.

In most cases, nothing, since non player prefabs have no classifieds.

Link to comment
Share on other sites

@Mario384,

 

That triggers when the player presses any key, right?

But only activates with the KEY_X.

 

You don't need to run any replica changes or anything, you only need the client to trigger a code change in the server.

 

Imagine if one client could run code that modifies replicas and that info gets to the server.

Cheating galore, you wouldn't need a mod that allows that.

 

As such, what you do is put the server code inside a AddModRPCHandler.

And inside a OnKeyAbility, you put a SendModRPC, so the client tells the server to do something.

AddModRPCHandler(modname, "triggerchange", function(player)	local inst = player	-- code between "if TheWorld.ismastersim then" and "else" goes hereend)local function OnKeyAbility(inst, data)	if inst == GLOBAL.ThePlayer and data.key == GLOBAL.KEY_X then		SendModRPCToServer(MODRPC[modname]["triggerchange"])	endend

The OnKeyAbility will presumably be handled by a key event, yes?

 

Also, there's no such DoDelta function in health_replica, you can check what you can use from there.

In most cases, nothing, since non player prefabs have no classifieds.

 

Okay, the code I posted above was most likely not the best example now I look at it.  See, I have Z, X, and C usually do this:

local TAUNT = GLOBAL.Action()TAUNT.str = "Taunt"TAUNT.id = "TAUNT"TAUNT.fn = function(act)	if not act.target:HasTag("busy") then	act.target.sg:GoToState("taunt")endendAddAction(TAUNT) local ABILITY = GLOBAL.Action()ABILITY.str = "Ability"ABILITY.id = "ABILITY"ABILITY.fn = function(act)	if not act.target:HasTag("busy") then	act.target.sg:GoToState("ability")endendAddAction(ABILITY)local SECONDARY = GLOBAL.Action()SECONDARY.str = "Secondary"SECONDARY.id = "SECONDARY"SECONDARY.fn = function(act)	if not act.target:HasTag("busy") then	act.target.sg:GoToState("secondary")endendAddAction(SECONDARY)  

And a normal mob prefab would have this :

local function OnKeyTaunt(inst, data)    if data.inst == ThePlayer then        if data.key == KEY_Z then             if TheWorld.ismastersim then                BufferedAction(inst, inst, ACTIONS.TAUNT):Do()                -- Since we are the server, do the action on the server.            else                SendRPCToServer(RPC.DoWidgetButtonAction, ACTIONS.TAUNT.code, inst, ACTIONS.TAUNT.mod_name)            end        end    endendlocal function OnKeyAbility(inst, data)    if data.inst == ThePlayer then        if data.key == KEY_X then             if TheWorld.ismastersim then                BufferedAction(inst, inst, ACTIONS.ABILITY):Do()                -- Since we are the server, do the action on the server.            else                SendRPCToServer(RPC.DoWidgetButtonAction, ACTIONS.ABILITY.code, inst, ACTIONS.ABILITY.mod_name)            end        end    endendlocal function OnKeySecondary(inst, data)    if data.inst == ThePlayer then        if data.key == KEY_C then             if TheWorld.ismastersim then                BufferedAction(inst, inst, ACTIONS.SECONDARY):Do()                -- Since we are the server, do the action on the server.            else                SendRPCToServer(RPC.DoWidgetButtonAction, ACTIONS.SECONDARY.code, inst, ACTIONS.SECONDARY.mod_name)            end        end    endend

But, that part of code from last post was from my pigplayer prefab.  Here's the full code:

local function OnKeyTaunt(inst, data)    if data.inst == ThePlayer then        if data.key == KEY_Z then             if TheWorld.ismastersim then    inst:RemoveTag("werepig")    inst:RemoveTag("guard")    inst:SetStateGraph("SGpigplayer")	inst.AnimState:SetBuild("pig_build")        if inst.components.hauntable then         inst.components.hauntable.haunted = false         inst.components.hauntable.cooldown_on_successful_haunt = true    end    inst.components.combat:SetDefaultDamage(TUNING.PIG_DAMAGE)    inst.components.combat:SetAttackPeriod(TUNING.PIG_ATTACK_PERIOD)    inst.components.combat:SetKeepTargetFunction(NormalKeepTargetFn)    inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED    inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED            inst.components.lootdropper:SetLoot({})    inst.components.lootdropper:AddRandomLoot("meat",3)    inst.components.lootdropper:AddRandomLoot("pigskin",1)    inst.components.lootdropper.numrandomloot = 1        inst.components.trader:Enable()    inst.components.talker:StopIgnoringAll()    inst.components.combat:SetHurtSound(nil)else    inst:RemoveTag("werepig")    inst:RemoveTag("guard")    inst:SetStateGraph("SGpigplayer")	inst.AnimState:SetBuild("pig_build")        if inst.components.hauntable then         inst.components.hauntable.haunted = false         inst.components.hauntable.cooldown_on_successful_haunt = true    end    inst.components.combat:SetDefaultDamage(TUNING.PIG_DAMAGE)    inst.components.combat:SetAttackPeriod(TUNING.PIG_ATTACK_PERIOD)    inst.components.combat:SetKeepTargetFunction(NormalKeepTargetFn)    inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED    inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED            inst.components.lootdropper:SetLoot({})    inst.components.lootdropper:AddRandomLoot("meat",3)    inst.components.lootdropper:AddRandomLoot("pigskin",1)    inst.components.lootdropper.numrandomloot = 1        inst.components.trader:Enable()    inst.components.talker:StopIgnoringAll()    inst.components.combat:SetHurtSound(nil)            end        end    endendlocal function OnKeyAbility(inst, data)    if data.inst == ThePlayer then        if data.key == KEY_X then     if TheWorld.ismastersim then    inst:RemoveTag("werepig")    inst:AddTag("guard")    inst:SetStateGraph("SGpigplayer")	inst.AnimState:SetBuild("pig_guard_build")        if inst.components.hauntable then         inst.components.hauntable.haunted = false         inst.components.hauntable.cooldown_on_successful_haunt = true    end    inst.components.combat:SetDefaultDamage(TUNING.PIG_GUARD_DAMAGE)    inst.components.combat:SetAttackPeriod(TUNING.PIG_GUARD_ATTACK_PERIOD)    inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED    inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED	inst.components.health:DoDelta(-40, nil, nil, nil, nil, true)        inst.components.lootdropper:SetLoot({})    inst.components.lootdropper:AddRandomLoot("meat",3)    inst.components.lootdropper:AddRandomLoot("pigskin",1)    inst.components.lootdropper.numrandomloot = 1    inst.components.trader:Enable()    inst.components.talker:StopIgnoringAll()    inst.components.follower:SetLeader(nil)    inst.components.combat:SetHurtSound(nil) else    inst:RemoveTag("werepig")    inst:AddTag("guard")    inst:SetBrain(guardbrain)    inst:SetStateGraph("SGpigplayer")	inst.AnimState:SetBuild("pig_guard_build")        if inst.components.hauntable then         inst.components.hauntable.haunted = false         inst.components.hauntable.cooldown_on_successful_haunt = true    end    inst.components.combat:SetDefaultDamage(TUNING.PIG_GUARD_DAMAGE)    inst.components.combat:SetAttackPeriod(TUNING.PIG_GUARD_ATTACK_PERIOD)    inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED    inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED        inst.components.lootdropper:SetLoot({})    inst.components.lootdropper:AddRandomLoot("meat",3)    inst.components.lootdropper:AddRandomLoot("pigskin",1)    inst.components.lootdropper.numrandomloot = 1    inst.components.trader:Enable()    inst.components.talker:StopIgnoringAll()    inst.components.follower:SetLeader(nil)    inst.components.combat:SetHurtSound(nil)            end        end    endendlocal function OnKeySecondary(inst, data)    if data.inst == ThePlayer then        if data.key == KEY_C then             if TheWorld.ismastersim then    inst:AddTag("werepig")    inst:RemoveTag("guard")    inst:SetStateGraph("SGwerepigplayer")    inst.AnimState:SetBuild("werepig_build")        inst.components.combat:SetDefaultDamage(TUNING.WEREPIG_DAMAGE)    inst.components.combat:SetAttackPeriod(TUNING.WEREPIG_ATTACK_PERIOD)    inst.components.locomotor.runspeed = TUNING.WEREPIG_RUN_SPEED     inst.components.locomotor.walkspeed = TUNING.WEREPIG_WALK_SPEED	inst.components.health:DoDelta(-40, nil, nil, nil, nil, true)        inst.components.lootdropper:SetLoot({"meat","meat", "pigskin"})    inst.components.lootdropper.numrandomloot = 0            inst.components.trader:Disable()    inst.components.follower:SetLeader(nil)    inst.components.talker:IgnoreAll()    inst.components.combat:SetHurtSound("dontstarve/creatures/werepig/hurt")            else    inst:AddTag("werepig")    inst:RemoveTag("guard")    inst:SetStateGraph("SGwerepigplayer")    inst.AnimState:SetBuild("werepig_build")        inst.components.combat:SetDefaultDamage(TUNING.WEREPIG_DAMAGE)    inst.components.combat:SetAttackPeriod(TUNING.WEREPIG_ATTACK_PERIOD)    inst.components.locomotor.runspeed = TUNING.WEREPIG_RUN_SPEED     inst.components.locomotor.walkspeed = TUNING.WEREPIG_WALK_SPEED        inst.components.lootdropper:SetLoot({"meat","meat", "pigskin"})    inst.components.lootdropper.numrandomloot = 0            inst.components.trader:Disable()    inst.components.follower:SetLeader(nil)    inst.components.talker:IgnoreAll()    inst.components.combat:SetHurtSound("dontstarve/creatures/werepig/hurt")            end        end    endend   local common_postinit = function(inst) 	inst.soundsname = "wilson"	inst:AddComponent("keyhandler")	inst:ListenForEvent("keypressed", OnKeyTaunt)	inst:ListenForEvent("keypressed", OnKeyAbility)	inst:ListenForEvent("keypressed", OnKeySecondary)	inst.MiniMapEntity:SetIcon( "pigplayer.tex" )	inst:DoTaskInTime(0, function()   if ThePlayer then      ThePlayer:EnableMovementPrediction(false)   endend)end

For pig, Taunt made him a regular pig, Ability made him a pig guard and USED TO check to see if his health was above 40 before he could trigger the ability, and took a health penalty, and Secondary makes him a werepig and also used to check for health and do the same penalty.  My main question was if I could apply the penalty for both client and serverhosting players, along with checking if his health was over a certain amount before activating it.

 

Edited by Mario384
Link to comment
Share on other sites

@Mario384, yeah, you just do everything in the server. If you modify the penalty of the health component of a player entity in the server then it will trigger client changes, so you don't have to worry there.

 

Your normal mob prefab code is what should be happening: if you are the server, you run code, if not, you send a RPC to make the server run the code. When the RPC arrives, you check for the health and see if you can apply the penalty.

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...