Muskar

GLOBAL.ThePlayer inaccessible PlayerPostInit

Recommended Posts

Muskar    8

I get the following error when I run a barebones mod:

 

Quote

[...]/modmain.lua"]:2: attempt to index field 'ThePlayer' (a nil value)

Here's the code:

AddPlayerPostInit(function(player)
    GLOBAL.ThePlayer:ListenForEvent("itemget", function(slot, item, src_pos) print('HelloWorld heard itemget called') end)
	--the below code works fine as a replacement though
    --player:ListenForEvent("itemget", function(slot, item, src_pos) print('HelloWorld heard itemget called') end)
end)

I've seen this referenced many times in similar fashion so I'm having trouble understanding the lifecycle of these global variables.

I thought AddPlayerPostInit was called after the player instance is initialized and that this would also initialize the GLOBAL.ThePlayer reference.

I'd appreciate insight into what I've misunderstood. Since I know very little at this point, that's of course going to be a lot, but I intend to create a fairly sizable UI client-only mod so I'm here to learn.

I still have a million questions about the API and lifecycle of various things and I'm also wondering if there's any other information available than this forum and the subforum?

modinfo.lua

Edited by Muskar
Added modinfo in case anyone is curious
  • Like 1

Share this post


Link to post
Share on other sites
CarlZalph    4761

ThePlayer is the client's version of the player entity they have control over.

For the server side you never use ThePlayer in any part of the code.

 

In your case here, the argument being passed in the function "player" is what you would use.

  • Like 3

Share this post


Link to post
Share on other sites
Muskar    8
1 hour ago, CarlZalph said:

ThePlayer is the client's version of the player entity they have control over.

For the server side you never use ThePlayer in any part of the code.

 

In your case here, the argument being passed in the function "player" is what you would use.

Thanks for the differentiation between server and client, but I'm still not following. Teach a man to fish, right?

I'm guessing this means that all the events happen server-side? Are there no client-side events equivalent to getting an item?

And how would you figure such questions out without submitting question after question on these forums or reading through many unrelated questions?

...Sorry, perhaps a bit of a loaded question :oops: Bare with me, I just feel a little impatient and eager to get to the point where creativity and design are the only limits again. I'm trying to control myself.

 

Share this post


Link to post
Share on other sites
CarlZalph    4761
17 minutes ago, Muskar said:

Teach a man to fish, right?

I'm guessing this means that all the events happen server-side? Are there no client-side events equivalent to getting an item?

And how would you figure such questions out without submitting question after question on these forums or reading through many unrelated questions?

 

I learned everything I know about DST's codebase by looking over it.  There really is no API documentation other than the code there.

Events happen on both sides but you have to look where they're generated from.  If it's a server-side component that's pushing the event, then the client won't know of it at all.

It's a loop of 'I know what I want to do' to 'searching in files recursively' over the scripts folder to try to lure out what you're after.

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites
Muskar    8
1 hour ago, CarlZalph said:

I learned everything I know about DST's codebase by looking over it.  There really is no API documentation other than the code there.

Events happen on both sides but you have to look where they're generated from.  If it's a server-side component that's pushing the event, then the client won't know of it at all.

It's a loop of 'I know what I want to do' to 'searching in files recursively' over the scripts folder to try to lure out what you're after.

Much appreciated. Just to clarify, so I searched scripts for

:PushEvent("itemget"

and found Container and Inventory to be the sources. How do I determine that they're server-side components? I feel sort of clued in by the fact that it contains logic that a client has no business deciding. But can components never have both a client and a server side?

I'm surprised there's no further documentation than the few scattered tutorials. Something sort of like this. There seems to be a fair amount of activity here, so it seems warranted to do. Down the line I'd certainly be happy to create such documentation to get things rolling a bit more.

EDIT:
I just realized that my mod is client only, so how do I even have access to this event if it's server-side? That doesn't seem to make sense to me

Edited by Muskar
  • Like 1

Share this post


Link to post
Share on other sites
Bigfootmech    21
4 hours ago, Muskar said:

I just realized that my mod is client only, so how do I even have access to this event if it's server-side? That doesn't seem to make sense to me

I agree hehe

Only recently started modding DST too. And the codebase seems pretty dense so far.

Especially with "what's at the client's/server side" https://forums.kleientertainment.com/applications/core/interface/file/attachment.php?id=307150

Spoiler


print("EASILY SEARCHABLE STRING")

print("isDedicated")
print(GLOBAL.TheNet:IsDedicated()) -- is server (whether it's fully loaded yet, or not)

print("isServer")
print(GLOBAL.TheNet:GetIsServer()) -- is server (fully loaded only / ready to accept clients)

print("isMasterSimulation")
print(GLOBAL.TheNet:GetIsMasterSimulation()) -- same result as "is server"??

print("isClient")
print(GLOBAL.TheNet:GetIsClient()) -- is a connected client

print("isHosting")
print(GLOBAL.TheNet:GetIsHosting()) -- is client host, or is server (has access to log files)

print("isServerClientHosted")
print(GLOBAL.TheNet:GetServerIsClientHosted()) -- actually does what it says

print("isServerDedicated")
print(GLOBAL.TheNet:GetServerIsDedicated()) -- seems broken??

print("isAdmin")
print(GLOBAL.TheNet:GetIsServerAdmin()) -- assuming this works?

print("isOwner")
print(GLOBAL.TheNet:GetIsServerOwner()) -- not fully tested
print(TheNet:GetIsServerOwner())

 

and events https://forums.kleientertainment.com/forums/topic/113241-help-with-event-listeners/?tab=comments#comment-1279683

Currently, I'm:

  • trying to figure out how similar mods do things if I can
  • using a lot of print statements
  • Figuring out how to "hook functions" (basically, override a function, and call the inherited one inside the override. But the LUA version of that) to figure out what's going on inside
  • Using the logs from Documents/Klei/DoNotStarveTogether/client_log.txt and Documents/Klei/DoNotStarveTogether/Cluster_X/Master and Caves/server_log.txt
  • text searching accross all files in "scripts" (<DST Install Folder>/data/databundles/scripts.zip extracted)
  • possibly text searching in the DST .exe file (<DST Install Folder>/bin/dontstarve_steam.exe) to confirm something in logs is C side

But it's incredibly slow going while I figure out how everything works.

Like one important thing is figuring out how the "env" is set up in mods.lua:267 and modutil.lua:171, ie: script "environment" for modmain. Apparently so new modders don't accidentally pollute global. Versus the "environment" of any things you subsequentally import (actual global, found in the modmain env.GLOBAL, assigned at mods.lua:296 (GLOBAL = _G) )

Edited by Bigfootmech
  • Like 1

Share this post


Link to post
Share on other sites
Muskar    8
4 minutes ago, Bigfootmech said:

I agree hehe

Only recently started modding DST too. And the codebase seems pretty dense so far.

Especially with "what's at the client's/server side" https://forums.kleientertainment.com/applications/core/interface/file/attachment.php?id=307150

  Hide contents



print("EASILY SEARCHABLE STRING")

print("isDedicated")
print(GLOBAL.TheNet:IsDedicated()) -- is server (whether it's fully loaded yet, or not)

print("isServer")
print(GLOBAL.TheNet:GetIsServer()) -- is server (fully loaded only / ready to accept clients)

print("isMasterSimulation")
print(GLOBAL.TheNet:GetIsMasterSimulation()) -- same result as "is server"??

print("isClient")
print(GLOBAL.TheNet:GetIsClient()) -- is a connected client

print("isHosting")
print(GLOBAL.TheNet:GetIsHosting()) -- is client host, or is server (has access to log files)

print("isServerClientHosted")
print(GLOBAL.TheNet:GetServerIsClientHosted()) -- actually does what it says

print("isServerDedicated")
print(GLOBAL.TheNet:GetServerIsDedicated()) -- seems broken??

print("isAdmin")
print(GLOBAL.TheNet:GetIsServerAdmin()) -- assuming this works?

print("isOwner")
print(GLOBAL.TheNet:GetIsServerOwner()) -- not fully tested
print(TheNet:GetIsServerOwner())

 

and events https://forums.kleientertainment.com/forums/topic/113241-help-with-event-listeners/?tab=comments#comment-1279683

Currently, I'm:

  • using a lot of print statements
  • Figuring out how to "value uphack" (basically, override a function, and call the inherited one inside the override. But the LUA version of it)
  • Using the logs from Documents/Klei/DoNotStarveTogether/client_log.txt and Documents/Klei/DoNotStarveTogether/Cluster_X/Master and Caves/server_log.txt
  • text searching accross all files in "scripts" (<DST Install Folder>/data/databundles/scripts.zip extracted)
  • possibly text searching in the DST .exe file (<DST Install Folder>/bin/dontstarve_steam.exe) to confirm something in logs is C side

But it's incredibly slow going while I figure out how everything works.

Like one important thing is figuring out how the "env" is set up in modutil.lua:171, ie: script "environment" for modmain. Versus the "environment" of any things you subsequentally import (env.GLOBAL)

Wow that's some great info. I feel like that dstNetTest.ods table could be turned into a great flowchart, but I'm trying to minimize yak shaving.

To add to your tips, I've been using the serpent lua library with the following settings to print out serialized versions of nested runtime tables (aka lua objects):

print(serpent.block(obj, {metatostring=false, maxlevel=3, maxnum=50, nocode=true}))

Just add the following to the top and put the serpent.lua file (and license file) into a "util" folder in your mod:

local serpent = require "util/serpent"

I just feel like I need to improve my debugging methods, because I'm usually spoiled with an actual debugger to step through code and look at variable values at break points. But I think I'm making progress. The biggest hurdle was to actually get a barebones mod to even work in the first place when the official sample mods didn't work.

Anyway, I digress... I'll probably see you around :)

  • Like 2

Share this post


Link to post
Share on other sites
CarlZalph    4761
33 minutes ago, Bigfootmech said:

Figuring out how to "value uphack" - I think that's what this community calls it (basically, override a function, and call the inherited one inside the override. But the LUA version of that)

But it's incredibly slow going while I figure out how everything works.

Upvalues are from the debug library and are used for when you can't access a variable directly.

Take for example:

somefile.lua:
local a = {}
a.val = 27
local b = {}
b.test = function(self)
    return a.val + 2
end
return b

You don't have direct access to 'a' and must get it through upvalues from the function 'b.test'.

What you've describe is function hooking where you either alter the arguments or the return value.

-- Given
a = function(b, c)
    return b + c
end

-- Do
local a_old = a
a = function(b, c, ...)
    -- Prehook to edit arguments
    b = b * 2
    -- Call original
    local rets = {a_old(b, c, ...)}
    -- Posthook to edit return values
    rets[1] = rets[1] * 3
    return unpack(rets)
end

Reason for the '...', the table packing and unpacking is to help future proof the function hook.  If Klei added more parameters then your mod won't break, assuming the parameters you're messing with didn't change order.

You could also freely not call the old function entirely if you wanted to block it, too.

 

Definitely is a slow going thing, lots of parts to understand and look over.

Edited by CarlZalph
  • Like 1
  • Thanks 2
  • Sanity 1

Share this post


Link to post
Share on other sites
Muskar    8
Just now, Bigfootmech said:

Ah yes, I had this issue too lol. But I'm dumb so I wrote my own "Printer" XD

I will check out this Serpent when I get the chance though!

printer.lua 9.17 kB · 0 downloads

I did the same thing until I spent around 30 minutes and then thinking to myself that I probably wasn't the first person to want to serialize tables in lua for debugging XD

Serpent is an extensive library. E.g. it prevents circular references and supports __tostring and __serialize too if you want it to, and long list of other things like sorting and byte handling etc.

Hehe, sound a bit like a commercial, but there's a long list of options and I just picked one that was recommended and it gets the job done for what I need at the moment.

  • Like 2

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