Sandertv / mcwss

A websocket server for Minecraft Bedrock Edition
https://godoc.org/github.com/Sandertv/mcwss
MIT License
118 stars 26 forks source link

How to remember the player? #6

Closed and7ey closed 4 years ago

and7ey commented 4 years ago

I would like to remember the player to be able to send commands from the external web server. (i.e. run HTTP and WSS servers at the same time, pass commands from HTTP to WSS)

It looks like the following steps should be followed:

  1. OnConnection - remember the player
  2. once a command is received from the HTTP request, identify the player, read player's details, and execute player.Exec

The question is what to store. The whole Player structure? Do I need to remember the whole structure of the player once again when he/she connects the second/third/etc. time?

Sandertv commented 4 years ago

What you could do is just maintain a map[playerName]*Player which is updated when a player joins and quits, then for the HTTP requests, look the player up in the map by the name and if it exists, call player.Exec.

and7ey commented 4 years ago

Thanks for so prompt reply. I am trying to do something similar. Just to experiment I try to do it with one player first -

Firstly, I save Player data to JSON file at onConnection event -

file, _ := json.MarshalIndent(player, "", " ")
_ = ioutil.WriteFile("test.json", file, 0644) 

Then, once the HTTP request is received, I try to read the data -

player := mcwss.Player{}
_ = json.Unmarshal([]byte(file), &player)
player.Say("hello") 

and it fails here.

(sorry, I am first time playing with Go)

Sandertv commented 4 years ago

Unfortunately you can't encode the player itself as JSON, as the WebSocket connection will be lost. Are your HTTP server and the WebSocket server running in separate processes or are they in the same program?

and7ey commented 4 years ago

It is the same program.

Sandertv commented 4 years ago

In that case you should be able to just have a package level var Players = map[playerName]*Player{} (probably including a sync.Mutex, or instead a sync.Map) to which you add players in OnConnection and remove from during disconnect. Then the HTTP server can, when a request is made, look a player up with a specific player name in that map (player, ok := Players["requestedName"]) and if it finds the player, do what it needs to do with it.

and7ey commented 4 years ago

My program is hosted on repl.it - the server is not always running. Believe, it will lose everything stored to Players, once the server is stopped.

Sandertv commented 4 years ago

That is correct, but players connected to it will be disconnected if the server shuts down anyway.

and7ey commented 4 years ago

You are right. Thanks.

Is it safe to use player.Name()?

  1. Does it always exist? Even for players without Xbox live profile?
  2. Is it always unique?
  3. What will happen if the same user connects from two different devices?

Is there any way to extract UserId? Probably it is better to use UserIdthan player.Name()?

Sandertv commented 4 years ago

The player name always exists and is unique for specific worlds, but not necessarily globally on different worlds if players playing on different local worlds are not logged in. The UserId I assume is also unique for players that are logged in (empty for non-logged in players), but I can't get access to that before a first event is fired.

and7ey commented 4 years ago

Thanks. First event looks ok. How can I extract the UserId + WorldId probably. Hopefully, it will be unique then.

Sandertv commented 4 years ago

After the first event you should be able to just do id := (*Player).UserID to get that and other data present in the properties.go

and7ey commented 4 years ago

Many thanks for your help! After further experiments, I've learned that UserId doesn't exist when the user didn't log in to XBox live. The player name (in most cases - Steve) will not work also. I will look for some other unique identifier.