Jump to content

Help with Server / Client Communication and client "Network View"


Recommended Posts

Hello, I'm the creator of the mod "Extended Indicators". The mod currently works fine for the host of the server however it fails to work properly for the client. I have done a lot of research into this issue and have found out various things relating to the issue. Please note that I am experienced with programming however I am new to the scene when it comes to creating mods for Don't Starve or Don't Starve Together.
 
Link to steam workshop page for the mod: http://steamcommunity.com/sharedfiles/filedetails/?id=358749986

Currently there is a maximum distance that the client computer can travel apart from other users before the server stops sending that client the information. I believe this by looking at the widget "player_common.lua" where between lines 430 and 438 it says the following.
 

[codesyntax]
-- "playerexited" is available on both server and client.
-- - On clients, this is pushed whenever a player entity is removed
-- locally because it has gone out of range of your network view.
-- - On servers, this message is identical to "ms_playerleft", since
-- players are always in network view range until they disconnect.

TheWorld:PushEvent("playerexited", inst)
if TheWorld.ismastersim then

    TheWorld:PushEvent("ms_playerleft", inst)
end

[/codesyntax]

 

The comment that was provided with the code shows me that this is called for clients when leaving the "network view" however servers have an infinite "network view" which explains why it works perfectly fine on the server.

There is also similar code for the player entering the "network view" between lines 262 and 275 of the "player_common.lua".

 

The component "playertargetindicator.lua" makes use of this and between lines 15 to 37 where it makes multiple calls to "playerexited" in order to remove the target indicators.

 

[codesyntax]

local PlayerTargetIndicator = Class(function(self, inst)
    self.inst = inst
 
    self.offScreenPlayers = {}
    self.onScreenPlayersLastTick = {}
    -- self.recentTargetRemoved = {}
    self.onplayerexited = function(world, player)
        OnPlayerExited(self, player)
    end
 
    inst:ListenForEvent("playerexited", self.onplayerexited, TheWorld)
    inst:StartUpdatingComponent(self)
end)
 
function PlayerTargetIndicator:OnRemoveFromEntity()
    if self.offScreenPlayers ~= nil then
        self.inst:RemoveEventCallback("playerexited", self.onplayerexited, TheWorld)
        for i, v in ipairs(self.offScreenPlayers) do
            self.inst.HUD:RemoveTargetIndicator(v)
        end
        self.offScreenPlayers = nil
    end
end
[/codesyntax]
 
Simply removing this code would not do anything as I believe the server no longer sends the X and Y positions for the players when outside the "network view" meaning even if the icon stays on screen it wouldn't be at all functional.
 
I'm sure there are some workarounds for this issue even if it's not ideal or entirely efficient. Since the server is always aware of other player information and their positions. I thought it may be possible to make use of "TheWorld.ismastersim" or "TheNet:GetIsServer()" to detect whether the user is the host and then use a function such as "PushEvent()" in order to send the X and Y positions to the client. This may then need me to rewrite how the indicators work in order to accept information from this source. (I discovered these methods from http://forums.kleientertainment.com/topic/47353-guide-getting-started-with-modding-dst-and-some-general-tips-for-ds-as-well as well as reading through various lua scripts.)
 
The seemingly easiest method would be to expand the "network view" of the client so that the client would never exit it and the indicators would never stop functioning. This of course would solve the issue however other issues may occur as a result. I imagine that while inside the network view the client is presented with a lot of information and if expanded to the entire world it would become highly inefficient for them. However even if this wasn't the case i'm not even sure the "network view" can be expanded as I cannot find any relating code in order to do so.
 
I have also thought perhaps the player X and Y values can be excluded from the "network view" and would constantly be pushed to the clients emulating being inside the "network view" but only for these particular variables. It may end up being very similar to the first method that I thought about.
 
I am not sure if it is of any additional help but I was interested in the component "playerprox.lua". This would return player positions and names depending on their position in the world. Lines 9 - 122 are particularly interesting and have been included for reference below.
 
[codesyntax]
--[[
  PlayerProx component can run in four possible ways
    - Any player within distance, all players outside distance (PlayerProx.AnyPlayer)
    - a specific player within and outside distance (PlayerProx.SpecificPlayer)
    - as soon as a player comes within range, start tracking that one for going out of distance and then relinquish tracking (PlayerProx.LockOnPlayer)
    - as soon as a player comes within range, start tracking that player and keep tracking that player (PlayerProx.LockAndKeepPlayer)
]]
 
local function AnyPlayer(inst)
    local playerprox = inst.components.playerprox
    if playerprox then
    if not playerprox.isclose then
      local x,y,z = inst.Transform:GetWorldPosition()
      local ents = TheSim:FindEntities(x,y,z, playerprox.near, {"player"}, {"notarget"})
      if #ents > 0 then
                playerprox.isclose = true
                if playerprox.onnear then
                    playerprox.onnear(inst, ents[1])
                end
      end
    else
      local x,y,z = inst.Transform:GetWorldPosition()
      local ents = TheSim:FindEntities(x,y,z, playerprox.far, {"player"}, {"notarget"})
      if #ents == 0 then
                playerprox.isclose = false
                if playerprox.onfar then
                    playerprox.onfar(inst)
                end
      end
    end
  end
end
 
--simply returns all near players
local function AllPlayers(inst)
    local playerprox = inst.components.playerprox
    if playerprox then
    local x,y,z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities( x,y,z, playerprox.near, {"player"}, {"notarget"} )
    if #ents > 0 then
            if playerprox.onnear then
                playerprox.onnear(inst, ents)
            end
    else
            if playerprox.onfar then
              playerprox.onfar(inst, ents)
          end
      end
  end
end
 
local function SpecificPlayer(inst)
    local playerprox = inst.components.playerprox
    if playerprox then
    if not playerprox.isclose then
      local player = playerprox.target
            local distsq = player:GetDistanceSqToInst(inst)
      if distsq < playerprox.near * playerprox.near then
        playerprox.isclose = true
                if playerprox.onnear then
                    playerprox.onnear(inst, player)
                end
      end
    else
      local player = playerprox.target
            local distsq = player:GetDistanceSqToInst(inst)
      if distsq > playerprox.far * playerprox.far then
        playerprox.isclose = false
                if playerprox.onfar then
                    playerprox.onfar(inst)
                end
      end
    end
  end
end
 
local function LockOnPlayer(inst)
    local playerprox = inst.components.playerprox
    if playerprox then
    if not playerprox.isclose then
      local x,y,z = inst.Transform:GetWorldPosition()
      local ents = TheSim:FindEntities(x,y,z, playerprox.near, {"player"}, {"notarget"})
      if #ents > 0 then
                playerprox.isclose = true
                if playerprox.onnear then
                    playerprox.onnear(inst, ents[1])
                end
        playerprox:SetTarget(ents[1])
      end
    else
      local player = playerprox.target
            local distsq = player:GetDistanceSqToInst(inst)
      if distsq > playerprox.far * playerprox.far then
        playerprox.isclose = false
        playerprox:SetTarget(nil)
                if playerprox.onfar then
                    playerprox.onfar(inst)
                end
      end
    end
  end
end
 
local function LockAndKeepPlayer(inst)
    local playerprox = inst.components.playerprox
    if playerprox then
    if not playerprox.isclose then
      local x,y,z = inst.Transform:GetWorldPosition()
      local ents = TheSim:FindEntities(x,y,z, playerprox.near, {"player"}, {"notarget"})
      if #ents > 0 then
                playerprox.isclose = true
                if playerprox.onnear then
                    playerprox.onnear(inst, ents[1])
                end
        playerprox:SetTargetMode(SpecificPlayer, ents[1], true)
      end
    else
      -- we should never get here
      assert(false)
    end
  end
end

[/codesyntax]

 

Functions such as these could potentially be used as an easier way to get the client position information and dump it to the clients. (A quick description about each function is include at the top of the code in comment form.)

 

It would be great if someone can help me solve the problem of getting it working on the client. If you have any additional information that you could provide that would help me or other people come up with solutions or improve existing ones that too would be very much appreciated.  :joyous:

Link to comment
Share on other sites

i'm not even sure the "network view" can be expanded as I cannot find any relating code in order to do so.

I don't believe you can, since it should be handled by the RakNet library in the C++ code.

 

I was actually thinking of making this mod myself, but I wanted to wait for Peter's custom game mode to get an idea of custom networking.

 

I'll come back to this, but I think the way to do it would be to have the host periodically update the clients on the absolute location of each player (it could be piggybacked onto player_classified with an AddClassPostConstruct, I believe), and pass that to playertargetindicator. 

 

Edit: Also one feature that might be nice in this mod is to only show the playertargetindicators while the scoreboard is up. That way they're not blocking UI when you're trying to do stuff, but you can bring them up on command. Not sure if you have something like this already, I haven't gone in to test it yet.

Edited by rezecib
Link to comment
Share on other sites

@legman111, At least with my tests between my two computers, I've managed to get it working for clients. I kinda ripped out all the code you were using so far, so I'll have to work on adding the config options back in, but I sent you a request on Steam so we can collaborate a little more easily on it.

 

One thing I want to look into adding tomorrow is support for campfires/firepits also showing up globally, so you can have smoke signals.

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