Implement server-side movement
This commit is contained in:
		@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user