Sign in to follow this  
MrDoge124

My code doesn't work when caves are enabled.

Recommended Posts

MrDoge124    3

In this previous post I managed to get a AOE stun attack working. However when I enable caves it doesn't work.


Turns out I think I'm missing something. It might be an action handler or some sort of net code which could be why it doesn't work when caves are enabled. It also doesn't crash or print anything to the logs, So I'm not sure why it isn't working.

This is the code. It's all contained inside a character lua file.

Spoiler

-- This function is rather complicated. You don't need to change anything in here.
local function ApplyBuff(buffData, instToBuff, ents)
    --print(ents)
    local buffUniqueName = buffData.uniqueName
    
    -- Cancel any existing duplicate timed buff.
    if instToBuff[buffUniqueName.."Task"] ~= nil then
        buffData.removeFunction(instToBuff, ents)
        instToBuff[buffUniqueName.."Task"]:Cancel()
        instToBuff[buffUniqueName.."Task"] = nil
    end
    
    -- Make sure that our buff is removed BEFORE the entity is saved (the server is closed or player quits).
    -- This is only necessary if you change variables which are saved, like current health, health-penalty, etc.
    -- Without it, you could apply a max-health buff, quit before it ran out, rejoin, and you would have kept
    -- the extra max health!
    local buffOnSave = function(self, inst, data)
        if inst[buffUniqueName.."Task"] ~= nil then
            buffData.removeFunction(inst, ents)
            inst[buffUniqueName.."Task"]:Cancel()
            inst[buffUniqueName.."Task"] = nil
        end
    end
    
    if instToBuff.OnSave ~= nil then
        local oldOnSave = instToBuff.OnSave
        instToBuff.OnSave = function(self, inst, data)
            buffOnSave(self, inst, data)
            oldOnSave(self, inst, data)
        end
    else
        instToBuff.OnSave = buffOnSave
    end
    
    -- Apply the buff.
    buffData.applyFunction(instToBuff, ents)
    
    -- Set up removal after a certain amount of time.
    instToBuff[buffUniqueName.."Task"] = instToBuff:DoTaskInTime(buffData.duration, function(inst)
        buffData.removeFunction(inst, ents)
        inst[buffUniqueName.."Task"] = nil
    end)
end


-- Now to make some buffs and put them in a Buffs-dictionary.
local Buffs = {"stunbuff",}

-- This function creates a new buff-data object and adds it to the Buffs-dictionary
-- with the uniqueName as key.
local function CreateBuff(uniqueName, duration, applyFunction, removeFunction)
    local newBuff = {}
    newBuff.uniqueName = uniqueName
    newBuff.duration = duration
    newBuff.applyFunction = applyFunction
    newBuff.removeFunction = removeFunction
    Buffs[uniqueName] = newBuff
end

-- This is an example of how to use the CreateBuff() function to create a new buff.
CreateBuff("stunbuff", 5.0,
    function(inst, ents)
        -- Apply all your buff code here
        -- Remember to always check whether the components you want to change are actually present on the
        -- entity before you try to mess with them.-- Store the current maxhealth in a variable.
        -- Add 0% speed boost in all situations
    for i, ent in ipairs(ents) do
		--local ds = ent.entity:GetDebugString()
		--local tagsstr = string.match(ds, "Tags: ([^\n]+)\n")
		--print(tagsstr)
        ent.components.locomotor:SetExternalSpeedMultiplier(inst, "stunkey", 0)
        if ent.brain then
            ent.brain:Stop()
        end
	end
    end,
    function(inst, ents)
        -- Revert your buff code here
        -- Remember to always check whether the components you want to change are actually present on the
        -- entity before you try to mess with them.
    -- Remove speed bonus.
    for i, ent in ipairs(ents) do
        ent.components.locomotor:RemoveExternalSpeedMultiplier(inst, "stunkey")
        if ent.brain then
            ent.brain:Start()
        end
    end
end
)

TheInput:AddKeyDownHandler(KEY_Z, function()
    local x, y, z = inst.Transform:GetWorldPosition()
    local range = 5
    local ents = TheSim:FindEntities(x, y, z, range, { "_combat"}, { "player", "playermerm", "playermonster", "structure", "INLIMBO", "playermonster", "NOCLICK"}, {"hostile", "pig", "merm", "monster", "prey", "animal", "bee",})
    --Find Entity nearby
    if not chargeincooldown and inst.components.sanity.current > 10 and table.getn(ents) > 0 then
    --If chargeincooldown is false and current sanity is larger than 10 and there is more that 0 entities nearby
        chargeincooldown = true --Disable the move by starting the cooldown
        ApplyBuff(Buffs["stunbuff"], inst, ents) --Run the apply buff function, inputting the entity table. God only knows how the rest of the function works.
        print("buff applied")
        inst.components.sanity.current = inst.components.sanity.current - 10 --Remove 10 sanity
    	inst.SoundEmitter:PlaySound("dontstarve/characters/green/stun")
        inst.Light:SetIntensity(0.75)
        inst.Light:SetFalloff(.1)
        inst.Light:SetRadius(1.5)--Deal with lighting
        inst:DoTaskInTime(0.2, function()
            inst.Light:Enable (false)--Hide light after 1 second
        end)
        --Reset everything after 15 seconds
        inst:DoTaskInTime(480, function()
		    print("recharged")
            inst.Light:SetIntensity(.75)
            inst.Light:SetFalloff(.5)
            inst.Light:SetRadius(0.8)
            inst.Light:Enable (true)
            chargeincooldown = false
        end)
    end
    end)

 

Any help would be appreciated.

Share this post


Link to post
Share on other sites
CarlZalph    4119

Actions done to server-side entities' components should be handled with an RPC, specifically a mod RPC, where the server then runs the callback for said RPC when a client does a thing.

Share this post


Link to post
Share on other sites
Ultroman    695

I would love for someone to do an RPC tutorial or example, so I can link to it in my tutorial compilation. Would really fill a void for everyone. Please tag me if you find one, @MrDoge124.

Share this post


Link to post
Share on other sites
MrDoge124    3

I've tried to add RPC handlers to the code but it doesn't work, I'm not sure what I'm doing.

Spoiler


AddModRPCHandler(modname, "GREEN_STUN", function()
    local x, y, z = inst.Transform:GetWorldPosition()
    local range = 5
    local ents = TheSim:FindEntities(x, y, z, range, { "_combat"}, { "player", "playermerm", "playermonster", "structure", "INLIMBO", "playermonster", "NOCLICK"}, {"hostile", "pig", "merm", "monster", "prey", "animal", "bee",})
    --Find Entity nearby
    if not chargeincooldown and inst.components.sanity.current > 10 and table.getn(ents) > 0 then
    --If chargeincooldown is false and current sanity is larger than 10 and there is more that 0 entities nearby
        chargeincooldown = true --Disable the move by starting the cooldown
        ApplyBuff(Buffs["stunbuff"], inst, ents) --Run the apply buff function, inputting the entity table. God only knows how the rest of the function works.
        print("buff applied")
        inst.components.sanity.current = inst.components.sanity.current - 10 --Remove 10 sanity
    	inst.SoundEmitter:PlaySound("dontstarve/characters/green/stun")
        inst.Light:SetIntensity(0.75)
        inst.Light:SetFalloff(.1)
        inst.Light:SetRadius(1.5)--Deal with lighting
        inst:DoTaskInTime(0.2, function()
            inst.Light:Enable (false)--Hide light after 1 second
        end)
        --Reset everything after 15 seconds
        inst:DoTaskInTime(480, function()
		    print("recharged")
            inst.Light:SetIntensity(.75)
            inst.Light:SetFalloff(.5)
            inst.Light:SetRadius(0.8)
            inst.Light:Enable (true)
            chargeincooldown = false
        end)
    end
    end)

    local greenhandlers = {}

    AddPlayerPostInit(function(inst)
        -- We hack
        inst:DoTaskInTime(0, function()
            -- We check if the character is ourselves
            -- So if another green player joins, we don't get the handlers
            if inst == GLOBAL.ThePlayer then
                -- If we are green
                if inst.prefab == "green" then
                    -- We create and store the key handlers
                    greenhandlers[1] = TheInput:AddKeyDownHandler(KEY_R, function()
                        SendModRPCToServer(MOD_RPC[modname]["GREENCHARGE"])
                    end)
                    -- If not, we go to the handlerslist and empty it
                    -- This is to avoid having the handlers if we switch characters in wilderness
                    -- If it's already empty, nothing changes
                    for k, v in pairs(greenhandlers) do
                        greenhandlers[k] = nil
                    end
                end
            end
        end)
    end)

 

I've found this error in the code.

Spoiler

[00:00:52]: [string "../mods/Green/scripts/prefabs/green.lua"]:244: variable 'Green' is not declared
LUA ERROR stack traceback:
=[C]:-1 in (global) error (C) <-1--1>
scripts/strict.lua:23 in () ? (Lua) <21-26>
   t = table: 08D51E68
   n = Green
../mods/Green/scripts/prefabs/green.lua:244 in (upvalue) master_postinit (Lua) <78-300>
   inst = 118130 -  (valid:true)
   speedadjustment = function - ../mods/Green/scripts/prefabs/green.lua:113
   ApplyBuff = function - ../mods/Green/scripts/prefabs/green.lua:156
   Buffs = table: 2049C948
   CreateBuff = function - ../mods/Green/scripts/prefabs/green.lua:205
scripts/prefabs/player_common.lua:1768 in (field) fn (Lua) <1395-1797>
   inst = 118130 -  (valid:true)
   gamemode = endless
scripts/mainfunctions.lua:258 in () ? (Lua) <247-289>
   name = green
   prefab = Prefab green - 

[00:00:52]: [string "../mods/Green/scripts/prefabs/green.lua"]:244: variable 'Green' is not declared
LUA ERROR stack traceback:
    =[C]:-1 in (global) error (C) <-1--1>
    scripts/strict.lua:23 in () ? (Lua) <21-26>
    ../mods/Green/scripts/prefabs/green.lua:244 in (upvalue) master_postinit (Lua) <78-300>
    scripts/prefabs/player_common.lua:1768 in (field) fn (Lua) <1395-1797>
    scripts/mainfunctions.lua:258 in () ? (Lua) <247-289>
	
[00:00:52]: Error decoding lua RPC sender
[00:00:52]: Warning: Widget:SetFocusFromChild is happening on a widget outside of the screen/widget hierachy. This will cause focus moves to fail. Is 	ScriptErrorWidget	not a screen?	
[00:00:52]: stack traceback:
	scripts/widgets/widget.lua:605 in (method) SetFocusFromChild (Lua) <602-627>
	scripts/widgets/widget.lua:624 in (method) SetFocusFromChild (Lua) <602-627>
	scripts/widgets/widget.lua:624 in (method) SetFocusFromChild (Lua) <602-627>
	scripts/widgets/widget.lua:656 in (method) SetFocus (Lua) <629-665>
	scripts/widgets/menu.lua:83 in (method) SetFocus (Lua) <74-85>
	scripts/widgets/scripterrorwidget.lua:109 in (method) OnUpdate (Lua) <102-119>
	scripts/update.lua:90 in () ? (Lua) <33-129>	

 

 

Edited by MrDoge124

Share this post


Link to post
Share on other sites
Auhrer    13

The cave really complicates everything, many mods or commands don't work with the cave in the world.
They could launch an easy way to link the mods to caves, or something, to fix this, official

Edited by Auhrer

Share this post


Link to post
Share on other sites
Ultroman    695
5 hours ago, Auhrer said:

The cave really complicates everything, many mods or commands don't work with the cave in the world.
They could launch an easy way to link the mods to caves, or something, to fix this, official

I get your frustration, but it's not really something they can just "fix". It's a multiplayer game, so you need to define how your mod should work for servers and clients. We have neat functions to split up the code, and we have RPC-calls to bridge the information sharing gap. If you want to make a mod, you need to tell the game how it should work on both the client and the server. That's just how it is when you're doing multiplayer games.

  • Thanks 1

Share this post


Link to post
Share on other sites
pickleplayer    175
On 5/2/2020 at 1:19 PM, Auhrer said:

The cave really complicates everything, many mods or commands don't work with the cave in the world.
They could launch an easy way to link the mods to caves, or something, to fix this, official

 

Ultroman's reply sums it up pretty well, but I'd like to also point out that it isn't "caves" that cause the issue. it's whenever the world isn't being hosted client-side (if i'm using the terminology correctly)

Running any world on a dedicated server, even without caves, will run into all of the same issues.

  • Like 1

Share this post


Link to post
Share on other sites
MrDoge124    3

I got it to work. I asked a friend and we fixed it.

So while poking around at another character, we discovered differences in the code that allowed us to reverse engineer a solution. With the error message telling us that “Inst” was a null value. Initially we thought that Inst was always in reference to the player whenever it’s called. THIS IS NOT THE CASE.
We then found that whenever we declared a function, "Inst" was not declared.

AddModRPCHandler(“Green”,"GREEN_STUN", function(---INST IS MISSING HERE---)

We changed “Inst” to “player” just to make the code more readable in the future and then changed any reference to Inst into player. In addition to this, several functions were missing declarations which we fixed by adding player.
 

AddModRPCHandler(“Green”,"GREEN_STUN", function(player)
Spoiler


TheInput = GLOBAL.TheInput
local KEY_Z = GLOBAL.KEY_Z

-- This function is rather complicated. You don't need to change anything in here.
local function ApplyBuff(buffData, instToBuff, ents)
    --print(ents)
    local buffUniqueName = buffData.uniqueName
    
    -- Cancel any existing duplicate timed buff.
    if instToBuff[buffUniqueName.."Task"] ~= nil then
        buffData.removeFunction(instToBuff, ents)
        instToBuff[buffUniqueName.."Task"]:Cancel()
        instToBuff[buffUniqueName.."Task"] = nil
    end
    
    -- Make sure that our buff is removed BEFORE the entity is saved (the server is closed or player quits).
    -- This is only necessary if you change variables which are saved, like current health, health-penalty, etc.
    -- Without it, you could apply a max-health buff, quit before it ran out, rejoin, and you would have kept
    -- the extra max health!
    local buffOnSave = function(self, player, data)
        if player[buffUniqueName.."Task"] ~= nil then
            buffData.removeFunction(player, ents)
            player[buffUniqueName.."Task"]:Cancel()
            player[buffUniqueName.."Task"] = nil
        end
    end
    
    if instToBuff.OnSave ~= nil then
        local oldOnSave = instToBuff.OnSave
        instToBuff.OnSave = function(self, player, data)
            buffOnSave(self, player, data)
            oldOnSave(self, player, data)
        end
    else
        instToBuff.OnSave = buffOnSave
    end
    
    -- Apply the buff.
    buffData.applyFunction(instToBuff, ents)
    
    -- Set up removal after a certain amount of time.
    instToBuff[buffUniqueName.."Task"] = instToBuff:DoTaskInTime(buffData.duration, function(player)
        buffData.removeFunction(player, ents)
        player[buffUniqueName.."Task"] = nil
    end)
end


-- Now to make some buffs and put them in a Buffs-dictionary.
local Buffs = {"stunbuff",}

-- This function creates a new buff-data object and adds it to the Buffs-dictionary
-- with the uniqueName as key.
local function CreateBuff(uniqueName, duration, applyFunction, removeFunction)
    local newBuff = {}
    newBuff.uniqueName = uniqueName
    newBuff.duration = duration
    newBuff.applyFunction = applyFunction
    newBuff.removeFunction = removeFunction
    Buffs[uniqueName] = newBuff
end

-- This is an example of how to use the CreateBuff() function to create a new buff.
CreateBuff("stunbuff", 5.0,
    function(player, ents)
        -- Apply all your buff code here
        -- Remember to always check whether the components you want to change are actually present on the
        -- entity before you try to mess with them.-- Store the current maxhealth in a variable.
        -- Add 0% speed boost in all situations
    for i, ent in ipairs(ents) do
		--local ds = ent.entity:GetDebugString()
		--local tagsstr = string.match(ds, "Tags: ([^\n]+)\n")
		--print(tagsstr)
        ent.components.locomotor:SetExternalSpeedMultiplier(player, "stunkey", 0)
        if ent.brain then
            ent.brain:Stop()
        end
	end
    end,
    function(player, ents)
        -- Revert your buff code here
        -- Remember to always check whether the components you want to change are actually present on the
        -- entity before you try to mess with them.
    -- Remove speed bonus.
    for i, ent in ipairs(ents) do
        ent.components.locomotor:RemoveExternalSpeedMultiplier(player, "stunkey")
        if ent.brain then
            ent.brain:Start()
        end
    end
end
)

AddModRPCHandler("Green", "GREEN_STUN", function(player)
    local x, y, z = player.Transform:GetWorldPosition()
    local range = 5
    local ents = TheSim:FindEntities(x, y, z, range, { "_combat"}, { "player", "playermerm", "playermonster", "structure", "INLIMBO", "playermonster", "NOCLICK"}, {"hostile", "pig", "merm", "monster", "prey", "animal", "bee",})
    --Find Entity nearby
    if not chargeincooldown and player.components.sanity.current > 10 and table.getn(ents) > 0 then
    --If chargeincooldown is false and current sanity is larger than 10 and there is more that 0 entities nearby
        chargeincooldown = true --Disable the move by starting the cooldown
        ApplyBuff(Buffs["stunbuff"], player, ents) --Run the apply buff function, inputting the entity table. God only knows how the rest of the function works.
        print("buff applied")
        player.components.sanity.current = player.components.sanity.current - 10 --Remove 10 sanity
    	player.SoundEmitter:PlaySound("dontstarve/characters/green/stun")
        player.Light:SetIntensity(0.75)
        player.Light:SetFalloff(.1)
        player.Light:SetRadius(1.5)--Deal with lighting
        player:DoTaskInTime(0.2, function()
            player.Light:Enable (false)--Hide light after 1 second
        end)
        --Reset everything after 15 seconds
        player:DoTaskInTime(480, function()
		    print("recharged")
            player.Light:SetIntensity(.75)
            player.Light:SetFalloff(.5)
            player.Light:SetRadius(0.8)
            player.Light:Enable (true)
            chargeincooldown = false
        end)
    end
end)

local greenhandlers = {}
AddPlayerPostInit(function(player)
    -- We hack
    player:DoTaskInTime(0, function()
        -- We check if the character is ourselves
        -- So if another green player joins, we don't get the handlers
        if player == GLOBAL.ThePlayer then
            -- If we are green
            if player.prefab == "green" then
                -- We create and store the key handlers
                greenhandlers[1] = TheInput:AddKeyDownHandler(KEY_Z, function()
                    SendModRPCToServer(MOD_RPC["Green"]["GREEN_STUN"])
                end)
                -- If not, we go to the handlerslist and empty it
                -- This is to avoid having the handlers if we switch characters in wilderness
                -- If it's already empty, nothing changes
                for k, v in pairs(greenhandlers) do
                    greenhandlers[k] = nil
                end
            end
        end
    end)
end)

 

Thanks for the help!

  • Thanks 1

Share this post


Link to post
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
Sign in to follow this