friendlyhj/ZenUtils

Suggestion; stable method to get client player object from UUID

Closed this issue · 3 comments

One issue ive ran into wiht my script with data manipulation is that some data requires both the player instance on the server and client to be updated at the same time to avoid halluciantions (as in one having it while the other dosent)

one instance of this is the onPlayerDeath event, which only runs on the server; lets say you want something to update about the player data on respawn but it requires a client update for things to work correctly to avoid desync/hallucination; you can use the UUID already in this addon to get the player object but is there a way to have this call the client object of the player as well

Note: while you can use IClient to fetch the client player object, this may cause issues in the event a non-client (dedicated server) tries to run the script.

To expand at what I mean by this:

Imagine you have something that regards updating data. In order for it to work correctly it needs to be done on both the client and server end; Having one side missing, causes the other to hallucinate or think that it's there but it's not (the best example of this is the glitch that occurs when mining netherrack so quickly that it creates a block that is there on the server but not to the client)

Some event methods run on both a server and the client; like onPlayerTick, but certain processes like onPlayerRespawn only have it run something on the server end. Using my SSB project as the fundamental example here; For something that I've implemented to update a certain portion of player data correctly without issues, requires me to send an update to both the server player and the client player by the event is occurring for; only for the server end to only receive an update an instance of its player and not the clients as well when it's ran. I was able to solve this issue by running the same line used to update the player but instead used the client package instead of the event. This is a general solution, but the only problem is is that this is not capable of working on a dedicated server.

Which brings us here; requesting a method that is dedicated server compatible with referencing a client of a player in situations like this.

The benefits of having such in place can allow for functions to be only executed on the server end and then apply any sort of changes needed to both the server and client; therefore saving resources instead of having the same thing run in parallel; especially if something that runs on the server and returning something differently (ie; damage events, returns something specific on server, clients returns as generic damage)

This is how I would go about using it or how it might look like

getClientFromUUID( string ) returns IClient of the provided UUID string, returns NULL if the string is invalid or if the client returned from sed UUID is not on the server nor has an instance of a Client.

events.onPlayerRespawn(function(event as {path to event here}) { 

print(event.player.world.isRemote());   //<---. Returns only false, lack of client task results in no secondary TRUE being returned

print(client.player.world.isRemote());  //<--- returns TRUE, but will more than likey break when used in a dedicated server

val playerUUID = event.player.uuid as string;

val playerClient = game.getClientFromUUID(playerUUID).player as IPlayer; //<--- acts as a means to get the client of a specific player if the player is on the server. Acts as a method of using IClient of a specific client.

playerClient.world.isRemote(); // <----- returns TRUE as its a client player; it is REMOTE

val thingToUpdate = {ForgeCaps:{smthing:[idk lel]} as IData;

event.player.updateNBT(thingToUpdate); //<--- updates player, however because its only a server ran task, ONLY UPDATES THE SERVER PLAYER, this can cause issues if things like ForgeCaps is not kept in sync like with other data

playerClient.updateNBT(thingToUpdate); //<--- Does the same as above but because its referencing the player on the CLIENT SIDE, it will send an update to ONLY the CLIENT PLAYER; used in combination with the line above effectively allows for both a Server and Client Update be sent in an event that only runs the task on the Server

});

I believe CraftTweaker scripts almost always only care server side logic. I wonder if this client-server data desync problem matters. Anyway, you can send data sync packet to client to solve the problem. As it is a RARE problem, I never write a documentation for it.

import mods.zenutils.NetworkHandler;

// registers the packet and defines how does client handle it
NetworkHandler.registerServer2ClientMessage("playerDataSync", function(player, byteBuf) {
    player.updateNBT(byteBuf.readData());
});

// in events, on server side, send a packet to client
NetworkHandler.sendTo("playerDataSync", player, function(byteBuf) {
    byteBuf.writeData(thingToUpdate);
});