diff --git a/server/plugin/control/control.go b/server/plugin/control/control.go index a31ac4f..e395611 100644 --- a/server/plugin/control/control.go +++ b/server/plugin/control/control.go @@ -3,6 +3,7 @@ package control import ( "context" "database/sql" + "fmt" "github.com/heroiclabs/nakama-common/runtime" "github.com/josephbmanley/family/server/plugin/entities" "github.com/josephbmanley/family/server/plugin/gamemap" @@ -17,6 +18,8 @@ type OpCode int64 const ( // OpCodeTileUpdate is used for tile updates OpCodeTileUpdate = 1 + // OpCodeUpdatePosition is used for player position updates + OpCodeUpdatePosition = 2 ) // Match is the object registered @@ -32,6 +35,15 @@ type MatchState struct { worldMap *gamemap.WorldMap } +// GetPrecenseList returns an array of current precenes in an array +func (state *MatchState) GetPrecenseList() []runtime.Presence { + precenseList := []runtime.Presence{} + for _, precense := range state.presences { + precenseList = append(precenseList, precense) + } + return precenseList +} + // MatchInit is called when a new match is created func (m *Match) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]interface{}) (interface{}, int, string) { @@ -79,8 +91,9 @@ func (m *Match) MatchJoin(ctx context.Context, logger runtime.Logger, db *sql.DB mState.presences[precense.GetUserId()] = precense player := entities.PlayerEntity{ - X: 16, - Y: 16, + X: 16, + Y: 16, + Presence: precense, } mState.players[precense.GetUserId()] = player @@ -108,18 +121,32 @@ func (m *Match) MatchLeave(ctx context.Context, logger runtime.Logger, db *sql.D } for _, presence := range presences { delete(mState.presences, presence.GetUserId()) + delete(mState.players, presence.GetUserId()) } return mState } // MatchLoop is code that is executed every tick func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, messages []runtime.MatchData) interface{} { - // Custom code to: - // - Process the messages received. - // - Update the match state based on the messages and time elapsed. - // - Broadcast new data messages to match participants. + mState, ok := state.(*MatchState) + if !ok { + logger.Error("Invalid match state on leave!") + return state + } + for _, message := range messages { + if message.GetOpCode() == OpCodeUpdatePosition { + player := mState.players[message.GetUserId()] - return state + if response, err := player.ParsePositionRequest(message.GetData()); err == nil { + player.UpdateBasedOnResponse(response) + dispatcher.BroadcastMessage(OpCodeUpdatePosition, []byte{}, mState.GetPrecenseList(), player.Presence, false) + logger.Info("Yes") + } else { + logger.Error(fmt.Sprintf("Failed to parse update pos request: %s", err.Error)) + } + } + } + return mState } // MatchTerminate is code that is executed when the match ends diff --git a/server/plugin/entities/entities.go b/server/plugin/entities/entities.go index 1b71a45..f27aacc 100644 --- a/server/plugin/entities/entities.go +++ b/server/plugin/entities/entities.go @@ -1,7 +1,55 @@ package entities +import ( + "encoding/json" + "fmt" + "github.com/heroiclabs/nakama-common/runtime" + "strconv" +) + // PlayerEntity is the go struct representing the player's location type PlayerEntity struct { - X int - Y int + Presence runtime.Presence + X float64 + Y float64 +} + +// PlayerPosResponse struct that represents client data +type PlayerPosResponse struct { + X string + Y string +} + +// ParsePositionRequest parses data from client +func (p *PlayerEntity) ParsePositionRequest(data []byte) (PlayerPosResponse, error) { + var response PlayerPosResponse + err := json.Unmarshal(data, &response) + return response, err +} + +//UpdateBasedOnResponse updates the player object based on a response object +func (p *PlayerEntity) UpdateBasedOnResponse(response PlayerPosResponse) error { + if fx, err := strconv.ParseFloat(response.X, 64); err != nil { + return err + } else { + p.X = fx + if fy, err := strconv.ParseFloat(response.Y, 64); err != nil { + return err + } else { + p.Y = fy + } + } + + return nil +} + +// GetPosJSON returns the player's position as a JSON object +func (p *PlayerEntity) GetPosJSON() ([]byte, error) { + playerMap := map[string]string{ + "player": p.Presence.GetUserId(), + "x": fmt.Sprintf("%f", p.X), + "y": fmt.Sprintf("%f", p.Y), + } + jsonData, err := json.Marshal(playerMap) + return jsonData, err } diff --git a/server/plugin/gamemap/gamemap.go b/server/plugin/gamemap/gamemap.go index 55bc45a..a4fa6f0 100644 --- a/server/plugin/gamemap/gamemap.go +++ b/server/plugin/gamemap/gamemap.go @@ -27,11 +27,10 @@ func (m WorldMap) GetJSONRegion(startX, endX, startY, endY int) ([]byte, error) for x := startX; x < endX; x++ { regionMap[x] = map[int]int{} for y := startY; y < endY; y++ { - if result, err := m.GetTile(x, y); err != nil { - return nil, err - } else { - regionMap[x][y] = result - } + + // GetTile and ignore out of bounds errors + result, _ := m.GetTile(x, y) + regionMap[x][y] = result } } @@ -40,8 +39,10 @@ func (m WorldMap) GetJSONRegion(startX, endX, startY, endY int) ([]byte, error) } // GetJSONRegionAround returns a JSON object of tile data from a center point -func (m WorldMap) GetJSONRegionAround(centerX, centerY, regionRadius int) ([]byte, error) { - jsonString, err := m.GetJSONRegion(centerX-regionRadius, centerX+regionRadius, centerY-regionRadius, centerY+regionRadius) +func (m WorldMap) GetJSONRegionAround(centerX float64, centerY float64, regionRadius int) ([]byte, error) { + var xCenter int = int(centerX) + var yCenter int = int(centerY) + jsonString, err := m.GetJSONRegion(xCenter-regionRadius, xCenter+regionRadius, yCenter-regionRadius, yCenter+regionRadius) return jsonString, err }