Jump to content

Client needs info that only exists on Host


Recommended Posts

Hello

I am trying to do something that I cannot get to work. The client needs information that only exists on the host.

For something that I'm trying to do, I need to give certain edible items new custom food types, but by doing that, they are no long edible to players.

To fix that, I can insert my new custom food type into the food group

table.insert(GLOBAL.FOODGROUP.OMNI.types, GLOBAL.FOODTYPE.UU_HONEY)

I technically can do this for all of the edible prefabs that I am changing the foodtype on, but I wanted a better, more forward-compatible method for doing this. Let's say Klei adds a new character that can eat honeycomb, currently honeycomb is not edible so I'm obviously not adding it into any existing food groups, only to my new custom foodgroups that I am creating.

 

What I would like to do is something like this:

local AddFoodTypeToFoodGroups(oldfoodtype, newfoodtype)
  for gkey,group in pairs(GLOBAL.FOODGROUP) do
    for tkey,type in pairs(group.types) do
      if type == oldfoodtype then
        table.insert(group.types, newfoodtype)
        break
      end
    end
  end
end

local MakeFoodType(inst, newfoodtype)
  if GLOBAL.TheWorld.ismastersim then
    -- edible component only exists on host
    local edible = inst.components.edible
    if edible ~= nil then
      AddFoodTypeToFoodGroup(edible.foodtype, newfoodtype)
    else
      inst:AddComponent("edible")
      edible = inst.components.edible
    end
    edible.foodtype = newfoodtype
  end
end

So what this does is if the edible prefab is already in an existing food group before i change the foodtype, i insert the new foodtype into that group. This way I don't mess with anyone's diets, everything they used to be able to eat they will still be able to eat, but now I can do what I need to do with the new food type.

 

The problem is, this only works for the host. Clients do not get the prompt to eat unless the correct foodtype is in the foodgroup, client side.

So the clients need information that only exists on the host. What can I do to fix this?

Link to comment
Share on other sites

Anyone have any idea how I can do this?

Maybe there's some way to do it with netvars? Maybe making an edible replica will allow the client to get access to this data?

I have no idea how to even use netvars or replicas so I don't even know know where to begin with even trying it out.

Link to comment
Share on other sites

On 1/5/2017 at 1:01 AM, HomShaBom said:

So the clients need information that only exists on the host. What can I do to fix this?

If you edit foodgroups or foodtypes, then the mod is all_clients_require_mod = true.

You should be editing the tables of both host and client.

Still, I don't really understand what you are trying to do.

A foodtype that everybody will be able to eat no matter the new foodgroups or foodtypes that are introduced?

local FOODTYPE = GLOBAL.FOODTYPE

FOODTYPE.UU_HONEY = "UU_HONEY"

AddPlayerPostInit(function(inst)
	local eater = inst.components.eater
	if eater then
		table.insert(eater.preferseating, FOODTYPE.UU_HONEY)
		table.insert(eater.caneat, FOODTYPE.UU_HONEY)
		inst:AddTag(FOODTYPE.UU_HONEY.."_eater")
	end
end)

Why would you give the an edible item a different foodtype?

If you want to make it not edible for monsters just edit the eater component.

Link to comment
Share on other sites

9 hours ago, DarkXero said:

If you edit foodgroups or foodtypes, then the mod is all_clients_require_mod = true.

You should be editing the tables of both host and client.

all_clients_require_mod is already set to true. The mod already runs on clients. That's not the issue. The problem is when the mod runs on a client, the prefab does not have an edible component. The component only exists on the host. So the client cannot see the original foodtype of the edible component.

 

 

 

 

Edited by HomShaBom
Link to comment
Share on other sites

47 minutes ago, HomShaBom said:

That's not the issue. The problem is when the mod runs on a client, the prefab does not have an edible component. The component only exists on the host. So the client cannot see the original foodtype of the edible component.

I see it now.

Having only changes propagate wouldn't work, because once the host edits the table, new clients wouldn't get the old edits.

That's why the game just setups the same tables on both sides, or uses a whole bunch of tags.

With tags:

local AddFoodTypeToFoodGroups(oldfoodtype, newfoodtype)
	for gkey, group in pairs(GLOBAL.FOODGROUP) do
		for tkey, type in pairs(group.types) do
			if type == oldfoodtype then
				table.insert(group.types, newfoodtype)
				break
			end
		end
	end
end

local function GetOldFoodType(food)
	for k, v in pairs(GLOBAL.FOODTYPE) do
		if food:HasTag("oldft_"..v) then
			return v
		end
	end
end

local function SyncType(food, newfoodtype)
	local oldft = GetOldFoodType(food)
	AddFoodTypeToFoodGroup(oldft, newfoodtype)
end

local MakeFoodType(inst, newfoodtype)
	if GLOBAL.TheWorld.ismastersim then
		-- edible component only exists on host
		local edible = inst.components.edible
		if edible ~= nil then
			AddFoodTypeToFoodGroup(edible.foodtype, newfoodtype)
			inst:AddTag("oldft_"..edible.foodtype)
		else
			inst:AddComponent("edible")
			edible = inst.components.edible
		end
		edible.foodtype = newfoodtype
	else
		-- give time for tag to propagate
		inst:DoTaskInTime(1, SyncType, newfoodtype)
	end
end

There are other options.

Turning the foodgroup table into a JSON string and sending it when a player joins or the table updates.

Turning edible into a replica. Or making a new replicable component and attaching it when you attach the edible one. In any case, you would need to map the foodtypes to use with integer netvariables. Or use string netvariables.

-- modmain

AddReplicableComponent("netfoodtype")

local AddFoodTypeToFoodGroups(oldfoodtype, newfoodtype)
	for gkey, group in pairs(GLOBAL.FOODGROUP) do
		for tkey, type in pairs(group.types) do
			if type == oldfoodtype then
				table.insert(group.types, newfoodtype)
				break
			end
		end
	end
end

local function SyncType(inst, newfoodtype)
	AddFoodTypeToFoodGroup(inst.replica.netfoodtype.foodtype:value(), newfoodtype)
end

local MakeFoodType(inst, newfoodtype)
	if GLOBAL.TheWorld.ismastersim then
		-- edible component only exists on host
		local edible = inst.components.edible
		if edible ~= nil then
			AddFoodTypeToFoodGroup(edible.foodtype, newfoodtype)

			inst:AddComponent("netfoodtype")
			inst.components.netfoodtype.foodtype = edible.foodtype
		else
			inst:AddComponent("edible")
			edible = inst.components.edible
		end
		edible.foodtype = newfoodtype
	else
		-- give time for info to propagate
		inst:DoTaskInTime(1, SyncType, newfoodtype)
	end
end


-- components/netfoodtype

local function onchangefoodtype(self, foodtype)
	self.inst.replica.netfoodtype.foodtype:set_local(foodtype)
	self.inst.replica.netfoodtype.foodtype:set(foodtype)
end

local netfoodtype = Class(function(self, inst)
	self.inst = inst
	-- self.foodtype = nil
	-- I dont want a default foodtype triggering the netvar
end,
nil,
{
	foodtype = onchangefoodtype,
})

return netfoodtype


-- components/netfoodtype_replica

local netfoodtype = Class(function(self, inst)
	self.inst = inst
	self.foodtype = net_string(inst.GUID, "food.netfood")
end)

return netfoodtype

If you want something forward-compatible as you say, you have to pay a price.

Also, I think you should check if newfoodtype is already in the types table before adding it.

Link to comment
Share on other sites

I think I understand this... When the host adds a tag to a prefab, does that tag get automatically synced to all clients, but after some delay so that's why the client does it as a task that runs at a later time?

Link to comment
Share on other sites

9 minutes ago, HomShaBom said:

I think I understand this... When the host adds a tag to a prefab, does that tag get automatically synced to all clients, but after some delay so that's why the client does it as a task that runs at a later time?

Yeah, tags are networked.

It's why on the base prefabs Klei puts tags on the entity before the pristine state- this is the state where no networking would be expected to happen after the prefab is fully initialized on both server and client.

With variable latency you'd ideally poll periodically for tag changes if you're expecting them to change.

Though it'd probably be better just to use a netvar at this point.

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...