Jump to content

Recommended Posts

I'm making a new character with a new 'tf2metal' stat.

I 'heavily' edited the health and health_replica components and made a badge for them.

It works fine serverside, but it crashes on clients, saying:

Quote

[string "../mods/ENGINEER/modmain.lua"]:122: attempt to index local 'metal' (a nil value)

So, it definitely can't see the replicable component.

It's my first time trying to make something like that (and not blindly copy-pasting), what am I doing wrong?

Also I'd like to use this new 'metal' stat for crafting, if that's possible. How to make it a crafting ingredient of sorts?

Here's the mod (since seems like nothing can be uploaded here atm :V)

Any help is appreciated!

Okay, I still can't figure out what's wrong. Every replicable component seems to attach itself in the same way as mine, I used AddReplicableComponent in modmain, yet the HUD still can't be built, since apparently client doesn't have a replica.

It works serverside, though.

20161102001034_1_by_randomanager-dan3wkp

I also searched for any info related to crafting using a custom stat.

Here's the post I found: Click!

I've tried using this method, but it doesn't seem to work for me. The recipe appears in crafting menu, but it ignores my character's stat. It always says I have 0 of the required ingredient.

Here's another iteration of my mod if anyone would want to try to help me out: New version

Just wanted to chime in and say, as far as I'm aware nothing would have broken the code in the post you linked where I helped the other person get it set up. If it's not working for you, more than likely you made a mistake somewhere. This is very likely due to the changes in the forum breaking a lot of the older posts with code in them.

You could download the Chakra mod and look at it in order to determine what might be the cause of your issue as that mod is still working as far as I can tell, based on comments.

3 hours ago, Kzisor said:

Just wanted to chime in and say, as far as I'm aware nothing would have broken the code in the post you linked where I helped the other person get it set up. If it's not working for you, more than likely you made a mistake somewhere. This is very likely due to the changes in the forum breaking a lot of the older posts with code in them.

You could download the Chakra mod and look at it in order to determine what might be the cause of your issue as that mod is still working as far as I can tell, based on comments.

That's possible, that's why I'm coming here. From my perspective everything seems like it should work, yet it doesn't.

I downloaded that Jutsu Mod to see how author did it. They used AddPlayerPostInit, because they wanted all players on the server to have this new stat added.

I'm making a single character, though. I tried doing the same thing (because maybe i'm adding replica/component wrong way), but server kept crashing for me, not leaving a word in its log file, (just "shutting down" and that's it), leaving me wondering what I'm doing wrong.

I tried AddComponentPostInit from modmain, because maybe functions in character's prefab weren't working or something. Nothing changed.

I also thought that maybe testing this on master (where the character works), the replica doesn't send information properly, but it returns correct numbers in the console.

I'm probably just doing it wrong, I'm not knowledgeable enough to know what's up.

9 hours ago, PanAzej said:

That's possible, that's why I'm coming here. From my perspective everything seems like it should work, yet it doesn't.

I downloaded that Jutsu Mod to see how author did it. They used AddPlayerPostInit, because they wanted all players on the server to have this new stat added.

I'm making a single character, though. I tried doing the same thing (because maybe i'm adding replica/component wrong way), but server kept crashing for me, not leaving a word in its log file, (just "shutting down" and that's it), leaving me wondering what I'm doing wrong.

I tried AddComponentPostInit from modmain, because maybe functions in character's prefab weren't working or something. Nothing changed.

I also thought that maybe testing this on master (where the character works), the replica doesn't send information properly, but it returns correct numbers in the console.

I'm probably just doing it wrong, I'm not knowledgeable enough to know what's up.

Take a look at the links in the post, I'm sure they might shed some more like on the issue.

Sorry, posting from phone so can only give a single link. However, the links in that post show how to create custom widgets which both tutorials still work to this day.

I'll try to recreate the tutorials on GitHub so they will keep their format.

13 hours ago, Kzisor said:

Take a look at the links in the post, I'm sure they might shed some more like on the issue.

Sorry, posting from phone so can only give a single link. However, the links in that post show how to create custom widgets which both tutorials still work to this day.

I'll try to recreate the tutorials on GitHub so they will keep their format.

Hm, thanks! I've already looked at your Wortox character, though. This time I removed postinit for player_classified, changed all stuff to go through prefab and added all "dirty" variables/values.

Previously all things went through player_classified that I edited through AddPrefabPostInit.

My badge code seems to be fine to me. Game still says that "tf2metal is nil", so it seems like there's no replica on client.

I've marked the place in which the error occurs.

local function StatusDisplaysPostInit(self)

	if self.owner:HasTag("tf2engineer") then
		local TF2MetalBadge = require "widgets/tf2metalbadge"
		self.tf2metalmeter = self:AddChild(TF2MetalBadge(self.owner))
		
		-- compatibility with Always On Status mod
		if KnownModIndex:IsModEnabled("workshop-376333686") then
			self.tf2metalmeter:SetPosition(-62, -52, 0)
		else	
	    	self.tf2metalmeter:SetPosition(-80, -40, 0)
		end
		
		local function OnSetPlayerMode(self)
			if self.onmetaldelta == nil then
				self.onmetaldelta = function(owner, data) self.tf2metalmeter:SetPercent(self.owner.replica.tf2metal:GetPercent(), self.owner.replica.tf2metal:Max()) end
				self.inst:ListenForEvent("metaldelta", self.onmetaldelta, self.owner)
				
                local metal = self.owner.replica.tf2metal 
--------------- self.tf2metalmeter:SetPercent(self.owner.replica.tf2metal:GetPercent(), metal:Max()) --<< here is the error, apparently
			end
		end
		
		local function OnSetGhostMode(self)
			if self.onmetaldelta ~= nil then
				self.inst:RemoveEventCallback("metaldelta", self.onmetaldelta, self.owner)
				self.onmetaldelta = nil
			end
		end

		self._SetGhostMode = self.SetGhostMode
		function self:SetGhostMode(ghostmode)
			self._SetGhostMode(self, ghostmode)
			if ghostmode then
				self.tf2metalmeter:Hide()
				OnSetGhostMode(self)
			else
				self.tf2metalmeter:Show()
				OnSetPlayerMode(self)
			end
		end
		
			OnSetPlayerMode(self)
	end
	return self

end
AddClassPostConstruct( "widgets/statusdisplays", StatusDisplaysPostInit)

Here's my current character's prefab:

local MakePlayerCharacter = require "prefabs/player_common"


local assets = {
    Asset("SCRIPT", "scripts/prefabs/player_common.lua"),
}
local prefabs = {}

local start_inv = {
"tf2wrench",
}

-- When loading or spawning the character
local function onload(inst)
 --
end

local function OnMetalDirty( inst, system )

	if inst ~= nil then

		local percent = inst.currentmetal:value() / inst.maxmetal:value()
		local currentmetal = inst.currentmetal:value()
		local maxmetal = inst.maxmetal:value()
		local data =
		{
			newpercent = percent,
			maxmetal = maxmetal,
			currentmetal = currentmetal,
		}
		inst:PushEvent("metaldirty", data)

	end
end

local function AddMetalSystem( inst )

	inst.maxmetal = net_ushortint(inst.GUID, "tf2metal.max", "currentmetaldirty")
	inst.currentmetal = net_ushortint(inst.GUID, "tf2metal.current", "currentmetaldirty")
	
	inst:ListenForEvent("currentmetaldirty", function( inst ) OnMetalDirty(inst) end)

end

-- This initializes for both the server and client. Tags can be added here.
local common_postinit = function(inst) 
	inst.MiniMapEntity:SetIcon( "tf2engineer.tex" )
	inst:AddTag("tf2engineer")
	
	AddMetalSystem( inst )
end

-- This initializes for the server only. Components are added here.
local master_postinit = function(inst)
	inst.soundsname = "wolfgang"
	
	inst.Transform:SetScale(1.07, 1.07, 1.07)
	
	inst.components.health:SetMaxHealth(125)
	inst.components.hunger:SetMax(125)
	inst.components.sanity:SetMax(250)
	
	if not inst.components.tf2metal then
		inst:AddComponent("tf2metal")
	end
	inst.components.tf2metal.maxmetal = 200
	inst.components.tf2metal.minmetal = 0
	inst.components.tf2metal.currentmetal = 200
	
    inst.components.combat.damagemultiplier = 0.75
	
	inst.components.builder.science_bonus = 1
	
	inst.components.hunger.hungerrate = 1 * TUNING.WILSON_HUNGER_RATE
	
	---	
	local _RemoveIngredients = inst.components.builder.RemoveIngredients
	function inst.components.builder:RemoveIngredients( ingredients, recname )

		local recipe = AllRecipes[recname]

		if recipe then
			for k,v in pairs(recipe.character_ingredients) do
				if v.type == CHARACTER_INGREDIENT.TF2METAL then
					self.inst.components.tf2metal:DoDelta(-v.amount)
				end
			end
		end

		_RemoveIngredients( self, ingredients, recname )
	end

	local _HasCharacterIngredient = inst.components.builder.HasCharacterIngredient
	function inst.components.builder:HasCharacterIngredient( ingredient )
		local _ret = _HasCharacterIngredient( self, ingredient )		
		if not _ret then
			if ingredient.type == CHARACTER_INGREDIENT.TF2METAL then
				if self.inst.components.tf2metal ~= nil then
					return self.inst.components.tf2metal.currentmetal >= ingredient.amount
				end
			end
		end

		return _ret		
	end
	---
	
	inst.OnLoad = onload
	
	inst.OnNewSpawn = function(inst)
		onload(inst)
		local helmet = SpawnPrefab("tf2hardhat")
		inst.components.inventory:Equip(helmet)
	end
	
end

return MakePlayerCharacter("tf2engineer", prefabs, assets, common_postinit, master_postinit, start_inv)

Badge/new stat seems to be working on master. Client crashes when character is selected.

Crafting recipe doesn't work on master as well (ignores stat, show that there's always 0 of it), but I guess that's to be expected if there's something so wrong that game crashes on client.

I've attached new iterations of component and replica to this post, maybe there's something wrong with them.

tf2metal.lua

tf2metal_replica.lua

Ill take a look at this when I get home, currently at work. Will update this post with my findings.

 

Edit:

The components look fine to me at first glance, I had some PC issues with my laptop when I got home; stupid Windows updates. What does your modmain.lua file look like at this moment in time?

Edited by Kzisor
20 hours ago, Kzisor said:

Ill take a look at this when I get home, currently at work. Will update this post with my findings.

 

Edit:

The components look fine to me at first glance, I had some PC issues with my laptop when I got home; stupid Windows updates. What does your modmain.lua file look like at this moment in time?

Thanks for looking into my stuff despite technical difficulties.

I've attached the modmain to this post.

modmain.lua

52 minutes ago, Kzisor said:

@PanAzej, could you also post the postinits.lua file that contains the error?

I probably screwed up and pasted only a part of it in modmain previously. My bad!

Here's the whole new mod version:

ENGINEER2.zip

Edited by PanAzej
Or... did I?

@PanAzej, so I have figured out the problem. Klei has made some changes to how character ingredients work. After actually getting a chance to test the Jutsu mod it actually does not work anymore. After spending about 3-4 hours on this particular issue today and about 2 the previous days trying to determine the root cause, I am afraid that it might not be doable 'at the moment' without some intervention from a Klei developer.

It seems the issue at hand is how they've modified the way character ingredients work. Previously, we could simply add a new line to the CHARACTER_INGREDIENT table like you're doing and it would automatically pick-up newly index items. However, now with the changes it seems they are only picking up the character ingredients a single time; when the save file loads but, before any mods are loaded.

It all fails and is tied to the following functionality which has been added since 'A New Reign'.

local is_character_ingredient = nil
function IsCharacterIngredient(ingredienttype)
    if is_character_ingredient == nil then
        is_character_ingredient = {}
        for k, v in pairs(CHARACTER_INGREDIENT) do
            is_character_ingredient[v] = true
        end
    end
    return ingredienttype ~= nil and is_character_ingredient[ingredienttype] == true
end

Recipes call this function to determine if the ingredient is a character ingredient. Unfortunately, if we override this function our custom ingredient type always returns nil. With that being said, our custom ingredient is added to the recipes.ingredients table instead of the recipe.character_ingredients table. A workaround would be to override the builder:CanBuild function, with a method similar to the following:

local _CanBuild = inst.components.builder.CanBuild
function inst.components.builder:CanBuild(recname)
	local _ret = _CanBuild(self, recname)

	if not _ret then
		local recipe = GetValidRecipe(recname)
		for i, v in ipairs(recipe.ingredients) do
			if v.type == GLOBAL.CHARACTER_INGREDIENT.TF2METAL then
				-- do your normal HasCharacterIngredient method here...
        	elseif not self.inst.components.inventory:Has(v.type, math.max(1, RoundBiasedUp(v.amount * self.ingredientmod))) then
        		return false
        	end
     	end
        for i, v in ipairs(recipe.character_ingredients) do
            if not self:HasCharacterIngredient(v) then
                return false
            end
        end
        for i, v in ipairs(recipe.tech_ingredients) do
            if not self:HasTechIngredient(v) then
                return false
            end
        end
		-- We've already determine that the ingredients failed before,
		-- so if we get here, we know they failed due to our custom ingredient,
		-- so return true ;)
		return true
	end
	return _ret
end

You would need to override the CanBuild function in the builder_replica as well, I'll leave that as an exercise for you to determine how to handle it.

With all this said, I've already created a method for handling this in the future from the development of the game and I will be posting a request for the code to get looked at and possibly be added in to a future update.

 

Edit:

You can view the full details of the issue here:

 

Edited by Kzisor
4 hours ago, Kzisor said:

@PanAzej, so I have figured out the problem. Klei has made some changes to how character ingredients work. After actually getting a chance to test the Jutsu mod it actually does not work anymore. After spending about 3-4 hours on this particular issue today and about 2 the previous days trying to determine the root cause, I am afraid that it might not be doable 'at the moment' without some intervention from a Klei developer.

It seems the issue at hand is how they've modified the way character ingredients work. Previously, we could simply add a new line to the CHARACTER_INGREDIENT table like you're doing and it would automatically pick-up newly index items. However, now with the changes it seems they are only picking up the character ingredients a single time; when the save file loads but, before any mods are loaded.

It all fails and is tied to the following functionality which has been added since 'A New Reign'.


local is_character_ingredient = nil
function IsCharacterIngredient(ingredienttype)
    if is_character_ingredient == nil then
        is_character_ingredient = {}
        for k, v in pairs(CHARACTER_INGREDIENT) do
            is_character_ingredient[v] = true
        end
    end
    return ingredienttype ~= nil and is_character_ingredient[ingredienttype] == true
end

Recipes call this function to determine if the ingredient is a character ingredient. Unfortunately, if we override this function our custom ingredient type always returns nil. With that being said, our custom ingredient is added to the recipes.ingredients table instead of the recipe.character_ingredients table. A workaround would be to override the builder:CanBuild function, with a method similar to the following:


local _CanBuild = inst.components.builder.CanBuild
function inst.components.builder:CanBuild(recname)
	local _ret = _CanBuild(self, recname)

	if not _ret then
		local recipe = GetValidRecipe(recname)
		for i, v in ipairs(recipe.ingredients) do
			if v.type == GLOBAL.CHARACTER_INGREDIENT.TF2METAL then
				-- do your normal HasCharacterIngredient method here...
        	elseif not self.inst.components.inventory:Has(v.type, math.max(1, RoundBiasedUp(v.amount * self.ingredientmod))) then
        		return false
        	end
     	end
        for i, v in ipairs(recipe.character_ingredients) do
            if not self:HasCharacterIngredient(v) then
                return false
            end
        end
        for i, v in ipairs(recipe.tech_ingredients) do
            if not self:HasTechIngredient(v) then
                return false
            end
        end
		-- We've already determine that the ingredients failed before,
		-- so if we get here, we know they failed due to our custom ingredient,
		-- so return true ;)
		return true
	end
	return _ret
end

You would need to override the CanBuild function in the builder_replica as well, I'll leave that as an exercise for you to determine how to handle it.

With all this said, I've already created a method for handling this in the future from the development of the game and I will be posting a request for the code to get looked at and possibly be added in to a future update.

 

Edit:

You can view the full details of the issue here:

 

Thanks for the insight, I'll definitely look into it and try to make it work!

Any idea why the character instantly crashes on client, though? On master game loads just fine.

I tried everything at this point, I have no idea what I'm doing wrong...

34 minutes ago, PanAzej said:

Thanks for the insight, I'll definitely look into it and try to make it work!

Any idea why the character instantly crashes on client, though? On master game loads just fine.

I tried everything at this point, I have no idea what I'm doing wrong...

I would need to have an updated version of the actual crash as the postinits file has been updated since the original error had bee posted.

On 6.11.2016 at 3:12 AM, Kzisor said:

I would need to have an updated version of the actual crash as the postinits file has been updated since the original error had bee posted.

It's mostly the same as before.

I've got recipes working thanks to you! The game doesn't show current metal amount properly, though, but I'm getting there.

Here's what I've put in character's prefab:

local _RemoveIngredients = inst.components.builder.RemoveIngredients
	function inst.components.builder:RemoveIngredients( ingredients, recname )

		local recipe = AllRecipes[recname]

		if recipe then
			for k,v in pairs(recipe.ingredients) do
				if v.type == CHARACTER_INGREDIENT.TF2METAL then
					self.inst.components.tf2metal:DoDelta(-v.amount)
				end
			end
		end

		_RemoveIngredients( self, ingredients, recname )
end
	
local _CanBuild = inst.components.builder.CanBuild
	function inst.components.builder:CanBuild(recname)
		local _ret = _CanBuild(recname)

		if not _ret and recname ~= nil then --game crashed because recname is nil for some reason
			local recipe = GetValidRecipe(recname)
			for i, v in ipairs(recipe.ingredients) do
				if v.type == CHARACTER_INGREDIENT.TF2METAL then
					if self.inst.components.tf2metal ~= nil then
						return self.inst.components.tf2metal.currentmetal >= v.amount
					end
				elseif not self.inst.components.inventory:Has(v.type, math.max(1, RoundBiasedUp(v.amount * self.ingredientmod))) then
					return false
				end
			end
			for i, v in ipairs(recipe.character_ingredients) do
				if not self:HasCharacterIngredient(v) then
					return false
				end
			end
			for i, v in ipairs(recipe.tech_ingredients) do
				if not self:HasTechIngredient(v) then
					return false
				end
			end

			return true
		end
		return _ret
end

GetValidRecipe in CanBuild returns nil for some reason. Not too sure why, since recipes are added properly, should be in AllRecipes table and should return their name/tab.

And here's current postinits file:

postinits.lua

And error:

20161106174229_1.jpg

Basically, the game tries to create a widget and set value, but it crashes on client, because component's replica doesn't exist for some reason. I've added it through AddReplicableComponent in modmain, but it doesn't change a thing.

Widget doesn't have this problem on master. Everything works as intended there.

Edited by PanAzej
On 11/7/2016 at 1:03 AM, PanAzej said:

It's mostly the same as before.

I've got recipes working thanks to you! The game doesn't show current metal amount properly, though, but I'm getting there.

Here's what I've put in character's prefab:

GetValidRecipe in CanBuild returns nil for some reason. Not too sure why, since recipes are added properly, should be in AllRecipes table and should return their name/tab.

And here's current postinits file:

postinits.lua

Basically, the game tries to create a widget and set value, but it crashes on client, because component's replica doesn't exist for some reason. I've added it through AddReplicableComponent in modmain, but it doesn't change a thing.

Widget doesn't have this problem on master. Everything works as intended there.

Sorry, haven't really had a chance to look into this until this weekend came around.

I have looked through your code and have looked through my Wortox mod which as far as I know still works. The major difference between the two is the fact that I am checking to see if the replica is actually there and you aren't.

self.tf2metalmeter:SetPercent(self.owner.replica.tf2metal:GetPercent(), metal:Max()) -- error?

compared to mine:

if self.owner.replica.leveler then
					leveldelta( { newpercent = self.owner.replica.leveler:GetPercent( level_system ),
						maxexperience = self.owner.replica.leveler:GetMaxExperience( level_system ),
						currentlevel = self.owner.replica.leveler:GetLevel( level_system ),
						maxlevel = self.owner.replica.leveler:GetMaxLevel( level_system ) })
				end

This is the only thing that I can think of that would be causing the issue with the widget.

Edited by Kzisor
22 hours ago, Kzisor said:

Sorry, haven't really had a chance to look into this until this weekend came around.

I have looked through your code and have looked through my Wortox mod which as far as I know still works. The major difference between the two is the fact that I am checking to see if the replica is actually there and you aren't.


self.tf2metalmeter:SetPercent(self.owner.replica.tf2metal:GetPercent(), metal:Max()) -- error?

compared to mine:


if self.owner.replica.leveler then
					leveldelta( { newpercent = self.owner.replica.leveler:GetPercent( level_system ),
						maxexperience = self.owner.replica.leveler:GetMaxExperience( level_system ),
						currentlevel = self.owner.replica.leveler:GetLevel( level_system ),
						maxlevel = self.owner.replica.leveler:GetMaxLevel( level_system ) })
				end

This is the only thing that I can think of that would be causing the issue with the widget.

Thanks for looking into it. Yup, this seems to fix the issue with crashing. Metal widget and crafting tab don't seem to update properly on client, but seems that replica exists and crafting consumes correct metal amount. I'll try to fix this stuff sometime.

Thanks once again!

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
×
  • Create New...