Merge pull request #25 from josephbmanley/feature/player
Character Controller!
This commit is contained in:
		@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					source_md5="8aa11b5093dfa6e5ad8586cfd48b6e88"
 | 
				
			||||||
 | 
					dest_md5="1922a2eba76828665cd680a720666fe8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								client/art/entities/player/dev_player.aseprite
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								client/art/entities/player/dev_player.aseprite
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								client/art/entities/player/dev_player.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								client/art/entities/player/dev_player.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 545 B  | 
							
								
								
									
										34
									
								
								client/art/entities/player/dev_player.png.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								client/art/entities/player/dev_player.png.import
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					[remap]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					importer="texture"
 | 
				
			||||||
 | 
					type="StreamTexture"
 | 
				
			||||||
 | 
					path="res://.import/dev_player.png-a3e490acef445550df71bca0ea3173dc.stex"
 | 
				
			||||||
 | 
					metadata={
 | 
				
			||||||
 | 
					"vram_texture": false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[deps]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					source_file="res://art/entities/player/dev_player.png"
 | 
				
			||||||
 | 
					dest_files=[ "res://.import/dev_player.png-a3e490acef445550df71bca0ea3173dc.stex" ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[params]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					compress/mode=0
 | 
				
			||||||
 | 
					compress/lossy_quality=0.7
 | 
				
			||||||
 | 
					compress/hdr_mode=0
 | 
				
			||||||
 | 
					compress/bptc_ldr=0
 | 
				
			||||||
 | 
					compress/normal_map=0
 | 
				
			||||||
 | 
					flags/repeat=0
 | 
				
			||||||
 | 
					flags/filter=false
 | 
				
			||||||
 | 
					flags/mipmaps=false
 | 
				
			||||||
 | 
					flags/anisotropic=false
 | 
				
			||||||
 | 
					flags/srgb=2
 | 
				
			||||||
 | 
					process/fix_alpha_border=true
 | 
				
			||||||
 | 
					process/premult_alpha=false
 | 
				
			||||||
 | 
					process/HDR_as_SRGB=false
 | 
				
			||||||
 | 
					process/invert_color=false
 | 
				
			||||||
 | 
					stream=false
 | 
				
			||||||
 | 
					size_limit=0
 | 
				
			||||||
 | 
					detect_3d=true
 | 
				
			||||||
 | 
					svg/scale=1.0
 | 
				
			||||||
@ -7,7 +7,7 @@ custom_features=""
 | 
				
			|||||||
export_filter="all_resources"
 | 
					export_filter="all_resources"
 | 
				
			||||||
include_filter=""
 | 
					include_filter=""
 | 
				
			||||||
exclude_filter=""
 | 
					exclude_filter=""
 | 
				
			||||||
export_path=""
 | 
					export_path="builds/Family.x86_64"
 | 
				
			||||||
patch_list=PoolStringArray(  )
 | 
					patch_list=PoolStringArray(  )
 | 
				
			||||||
script_export_mode=1
 | 
					script_export_mode=1
 | 
				
			||||||
script_encryption_key=""
 | 
					script_encryption_key=""
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								client/nodes/Systems/PlayerPuppeteer.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								client/nodes/Systems/PlayerPuppeteer.tscn
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					[gd_scene load_steps=3 format=2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ext_resource path="res://scripts/systems/PlayerPuppeteer.gd" type="Script" id=1]
 | 
				
			||||||
 | 
					[ext_resource path="res://nodes/entities/PlayerPuppet.tscn" type="PackedScene" id=2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="PlayerPuppeteer" type="Node2D"]
 | 
				
			||||||
 | 
					script = ExtResource( 1 )
 | 
				
			||||||
 | 
					puppet_template = ExtResource( 2 )
 | 
				
			||||||
							
								
								
									
										21
									
								
								client/nodes/entities/Player.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								client/nodes/entities/Player.tscn
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					[gd_scene load_steps=3 format=2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ext_resource path="res://art/entities/player/dev_player.png" type="Texture" id=1]
 | 
				
			||||||
 | 
					[ext_resource path="res://scripts/entities/Player.gd" type="Script" id=2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="Player" type="KinematicBody2D"]
 | 
				
			||||||
 | 
					script = ExtResource( 2 )
 | 
				
			||||||
 | 
					__meta__ = {
 | 
				
			||||||
 | 
					"_edit_group_": true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="Sprite" type="Sprite" parent="."]
 | 
				
			||||||
 | 
					texture = ExtResource( 1 )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="Camera2D" type="Camera2D" parent="."]
 | 
				
			||||||
 | 
					current = true
 | 
				
			||||||
 | 
					zoom = Vector2( 0.5, 0.5 )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="Line2D" type="Line2D" parent="."]
 | 
				
			||||||
 | 
					width = 4.0
 | 
				
			||||||
 | 
					default_color = Color( 1, 0, 0, 1 )
 | 
				
			||||||
							
								
								
									
										6
									
								
								client/nodes/entities/PlayerPuppet.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								client/nodes/entities/PlayerPuppet.tscn
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					[gd_scene load_steps=2 format=2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ext_resource path="res://art/entities/player/dev_player.png" type="Texture" id=1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="PlayerPuppet" type="Sprite"]
 | 
				
			||||||
 | 
					texture = ExtResource( 1 )
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -107,6 +107,14 @@ config/icon="res://icon.png"
 | 
				
			|||||||
Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd"
 | 
					Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd"
 | 
				
			||||||
ServerConnection="*res://scripts/singletons/ServerConnection.gd"
 | 
					ServerConnection="*res://scripts/singletons/ServerConnection.gd"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[input]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					move_to_cursor={
 | 
				
			||||||
 | 
					"deadzone": 0.5,
 | 
				
			||||||
 | 
					"events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null)
 | 
				
			||||||
 | 
					 ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[rendering]
 | 
					[rendering]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
quality/driver/driver_name="GLES2"
 | 
					quality/driver/driver_name="GLES2"
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										39
									
								
								client/scripts/entities/Player.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								client/scripts/entities/Player.gd
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					extends KinematicBody2D
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signal finished_moving
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export var base_movement_speed = 100
 | 
				
			||||||
 | 
					export(NodePath) var world
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onready var navigation : Navigation2D = get_node(world)
 | 
				
			||||||
 | 
					var path : PoolVector2Array
 | 
				
			||||||
 | 
					var move : bool = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _process(delta):
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						# Click for movement target
 | 
				
			||||||
 | 
						if Input.is_action_just_pressed("move_to_cursor"):
 | 
				
			||||||
 | 
							path = navigation.get_simple_path(global_position, get_global_mouse_position())
 | 
				
			||||||
 | 
							move = true
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if move:
 | 
				
			||||||
 | 
							move_along_path(base_movement_speed * delta)
 | 
				
			||||||
 | 
							ServerConnection.send_player_position(global_position)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func move_along_path(distance : float):
 | 
				
			||||||
 | 
						var start_point := global_position
 | 
				
			||||||
 | 
						for i in range(path.size()):
 | 
				
			||||||
 | 
							var distance_to_next := start_point.distance_to(path[0])
 | 
				
			||||||
 | 
							if distance <= distance_to_next and distance >= 0.0:
 | 
				
			||||||
 | 
								global_position = start_point.linear_interpolate(path[0], distance/distance_to_next)
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							elif distance < 0.0:
 | 
				
			||||||
 | 
								global_position = path[0]
 | 
				
			||||||
 | 
								emit_signal("finished_moving")
 | 
				
			||||||
 | 
								print("DONE")
 | 
				
			||||||
 | 
								move = false
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							distance -= distance_to_next
 | 
				
			||||||
 | 
							start_point = path[0]
 | 
				
			||||||
 | 
							path.remove(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,6 +1,9 @@
 | 
				
			|||||||
extends Node
 | 
					extends Node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
signal tile_update(tile_data)
 | 
					signal tile_update(tile_data)
 | 
				
			||||||
 | 
					signal player_joined(user_id)
 | 
				
			||||||
 | 
					signal player_left(user_id)
 | 
				
			||||||
 | 
					signal player_pos_update(user_id, pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const KEY := "defaultkey"
 | 
					const KEY := "defaultkey"
 | 
				
			||||||
const SERVER_ENDPOINT := "nakama.cloudsumu.com"
 | 
					const SERVER_ENDPOINT := "nakama.cloudsumu.com"
 | 
				
			||||||
@ -9,9 +12,11 @@ var _session : NakamaSession
 | 
				
			|||||||
var _client : NakamaClient = Nakama.create_client(KEY, SERVER_ENDPOINT, 7350, "http")
 | 
					var _client : NakamaClient = Nakama.create_client(KEY, SERVER_ENDPOINT, 7350, "http")
 | 
				
			||||||
var _socket : NakamaSocket
 | 
					var _socket : NakamaSocket
 | 
				
			||||||
var _precenses : Dictionary = {}
 | 
					var _precenses : Dictionary = {}
 | 
				
			||||||
 | 
					var _world_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum OPCODE {
 | 
					enum OPCODE {
 | 
				
			||||||
	tile_update = 1
 | 
						tile_update = 1,
 | 
				
			||||||
 | 
						update_position = 2
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func authenticate_async(email : String, password : String) -> NakamaException:
 | 
					func authenticate_async(email : String, password : String) -> NakamaException:
 | 
				
			||||||
@ -43,6 +48,7 @@ func connect_to_server_async() -> NakamaException:
 | 
				
			|||||||
	var result : NakamaAsyncResult = yield(_socket.connect_async(_session), "completed")
 | 
						var result : NakamaAsyncResult = yield(_socket.connect_async(_session), "completed")
 | 
				
			||||||
	if not result.is_exception():
 | 
						if not result.is_exception():
 | 
				
			||||||
		_socket.connect("received_match_state", self, "_on_socket_received_match_state")
 | 
							_socket.connect("received_match_state", self, "_on_socket_received_match_state")
 | 
				
			||||||
 | 
							_socket.connect("received_match_presence", self, "_on_received_match_presence")
 | 
				
			||||||
		_socket.connect("closed", self, "_on_socket_closed")
 | 
							_socket.connect("closed", self, "_on_socket_closed")
 | 
				
			||||||
		return null
 | 
							return null
 | 
				
			||||||
	return result.exception
 | 
						return result.exception
 | 
				
			||||||
@ -58,8 +64,11 @@ func join_world_async() -> Dictionary:
 | 
				
			|||||||
		print("Join match error: %s - %s" % [match_join_result.exception.status_code, match_join_result.exception.message])
 | 
							print("Join match error: %s - %s" % [match_join_result.exception.status_code, match_join_result.exception.message])
 | 
				
			||||||
		return {}
 | 
							return {}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
 | 
						_world_id = world.payload
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	for precense in match_join_result.presences:
 | 
						for precense in match_join_result.presences:
 | 
				
			||||||
		_precenses[precense.user_id] = precense
 | 
							_precenses[precense.user_id] = precense
 | 
				
			||||||
 | 
							emit_signal("player_joined", precense.user_id)
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	print("Joined matched with %s other players!" % _precenses.size())
 | 
						print("Joined matched with %s other players!" % _precenses.size())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -68,8 +77,25 @@ func join_world_async() -> Dictionary:
 | 
				
			|||||||
func _on_socket_closed():
 | 
					func _on_socket_closed():
 | 
				
			||||||
	_socket = null
 | 
						_socket = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _on_received_match_presence(match_precense : NakamaRTAPI.MatchPresenceEvent):
 | 
				
			||||||
 | 
						for precense in match_precense.joins:
 | 
				
			||||||
 | 
							print("%s joined the game!" % precense.username)
 | 
				
			||||||
 | 
							_precenses[precense.user_id] = precense
 | 
				
			||||||
 | 
							emit_signal("player_joined", precense.user_id)
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
						for precense in match_precense.leaves:
 | 
				
			||||||
 | 
							print("%s left the game!" % precense.username)
 | 
				
			||||||
 | 
							_precenses.erase(precense.user_id)
 | 
				
			||||||
 | 
							emit_signal("player_left", precense.user_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func _on_socket_received_match_state(match_state: NakamaRTAPI.MatchData):
 | 
					func _on_socket_received_match_state(match_state: NakamaRTAPI.MatchData):
 | 
				
			||||||
	match match_state.op_code:
 | 
						match match_state.op_code:
 | 
				
			||||||
		OPCODE.tile_update:
 | 
							OPCODE.tile_update:
 | 
				
			||||||
			emit_signal("tile_update", JSON.parse(match_state.data).result)
 | 
								emit_signal("tile_update", JSON.parse(match_state.data).result)
 | 
				
			||||||
 | 
							OPCODE.update_position:
 | 
				
			||||||
 | 
								var pos_data = JSON.parse(match_state.data).result
 | 
				
			||||||
 | 
								emit_signal("player_pos_update", pos_data["player"], Vector2(float(pos_data["x"]), float(pos_data["y"])))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func send_player_position(position : Vector2) -> void:
 | 
				
			||||||
 | 
						_socket.send_match_state_async(_world_id, OPCODE.update_position, JSON.print({X = str(position.x), Y = str(position.y)}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								client/scripts/systems/PlayerPuppeteer.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								client/scripts/systems/PlayerPuppeteer.gd
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					extends Node2D
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export(NodePath) var puppet_parent
 | 
				
			||||||
 | 
					export(Resource) var puppet_template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onready var puppet_parent_node : Node = get_node(puppet_parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var puppets : Dictionary = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _ready():
 | 
				
			||||||
 | 
						ServerConnection.connect("player_joined", self, "on_player_join")
 | 
				
			||||||
 | 
						ServerConnection.connect("player_left", self, "on_player_leave")
 | 
				
			||||||
 | 
						ServerConnection.connect("player_pos_update", self, "on_player_pos_update")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func on_player_join(user_id):
 | 
				
			||||||
 | 
						if user_id != ServerConnection._session.user_id:
 | 
				
			||||||
 | 
							var new_puppet : Node = puppet_template.instance()
 | 
				
			||||||
 | 
							new_puppet.name = "Player: " + user_id
 | 
				
			||||||
 | 
							puppet_parent_node.add_child(new_puppet)
 | 
				
			||||||
 | 
							puppets[user_id] = new_puppet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func on_player_leave(user_id):
 | 
				
			||||||
 | 
						if user_id != ServerConnection._session.user_id:
 | 
				
			||||||
 | 
							var player_puppet : Node = puppets[user_id]
 | 
				
			||||||
 | 
							player_puppet.queue_free()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func on_player_pos_update(user_id, pos):
 | 
				
			||||||
 | 
						if user_id != ServerConnection._session.user_id:
 | 
				
			||||||
 | 
							var player_puppet : Node2D = puppets[user_id]
 | 
				
			||||||
 | 
							player_puppet.global_position = pos
 | 
				
			||||||
@ -3,10 +3,14 @@ package control
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"database/sql"
 | 
						"database/sql"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"github.com/heroiclabs/nakama-common/runtime"
 | 
						"github.com/heroiclabs/nakama-common/runtime"
 | 
				
			||||||
 | 
						"github.com/josephbmanley/family/server/plugin/entities"
 | 
				
			||||||
	"github.com/josephbmanley/family/server/plugin/gamemap"
 | 
						"github.com/josephbmanley/family/server/plugin/gamemap"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const maxRenderDistance int = 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// OpCode represents a enum for valid OpCodes
 | 
					// OpCode represents a enum for valid OpCodes
 | 
				
			||||||
// used by the match logic
 | 
					// used by the match logic
 | 
				
			||||||
type OpCode int64
 | 
					type OpCode int64
 | 
				
			||||||
@ -14,6 +18,8 @@ type OpCode int64
 | 
				
			|||||||
const (
 | 
					const (
 | 
				
			||||||
	// OpCodeTileUpdate is used for tile updates
 | 
						// OpCodeTileUpdate is used for tile updates
 | 
				
			||||||
	OpCodeTileUpdate = 1
 | 
						OpCodeTileUpdate = 1
 | 
				
			||||||
 | 
						// OpCodeUpdatePosition is used for player position updates
 | 
				
			||||||
 | 
						OpCodeUpdatePosition = 2
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Match is the object registered
 | 
					// Match is the object registered
 | 
				
			||||||
@ -24,20 +30,27 @@ type Match struct{}
 | 
				
			|||||||
// Nakama match methods
 | 
					// Nakama match methods
 | 
				
			||||||
type MatchState struct {
 | 
					type MatchState struct {
 | 
				
			||||||
	presences map[string]runtime.Presence
 | 
						presences map[string]runtime.Presence
 | 
				
			||||||
 | 
						players   map[string]entities.PlayerEntity
 | 
				
			||||||
	inputs    map[string]string
 | 
						inputs    map[string]string
 | 
				
			||||||
	positions map[string]map[string]int
 | 
					 | 
				
			||||||
	names     map[string]string
 | 
					 | 
				
			||||||
	worldMap  *gamemap.WorldMap
 | 
						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
 | 
					// 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) {
 | 
					func (m *Match) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]interface{}) (interface{}, int, string) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	state := &MatchState{
 | 
						state := &MatchState{
 | 
				
			||||||
		presences: map[string]runtime.Presence{},
 | 
							presences: map[string]runtime.Presence{},
 | 
				
			||||||
		inputs:    map[string]string{},
 | 
							inputs:    map[string]string{},
 | 
				
			||||||
		positions: map[string]map[string]int{},
 | 
							players:   map[string]entities.PlayerEntity{},
 | 
				
			||||||
		names:     map[string]string{},
 | 
					 | 
				
			||||||
		worldMap:  gamemap.IntializeMap(),
 | 
							worldMap:  gamemap.IntializeMap(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tickRate := 10
 | 
						tickRate := 10
 | 
				
			||||||
@ -77,13 +90,24 @@ func (m *Match) MatchJoin(ctx context.Context, logger runtime.Logger, db *sql.DB
 | 
				
			|||||||
		// Add presence to map
 | 
							// Add presence to map
 | 
				
			||||||
		mState.presences[precense.GetUserId()] = precense
 | 
							mState.presences[precense.GetUserId()] = precense
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Set player spawn pos
 | 
							player := entities.PlayerEntity{
 | 
				
			||||||
		mState.positions[precense.GetUserId()] = map[string]int{"x": 16, "y": 16}
 | 
								X:        16,
 | 
				
			||||||
 | 
								Y:        16,
 | 
				
			||||||
 | 
								Presence: precense,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mState.names[precense.GetUserId()] = "User"
 | 
							if jsonObj, err := player.GetPosJSON(); err != nil {
 | 
				
			||||||
 | 
								logger.Error(err.Error())
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if sendErr := dispatcher.BroadcastMessage(OpCodeUpdatePosition, jsonObj, []runtime.Presence{precense}, player.Presence, true); sendErr != nil {
 | 
				
			||||||
 | 
									logger.Error(sendErr.Error())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mState.players[precense.GetUserId()] = player
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Get intial tile data around player
 | 
							// Get intial tile data around player
 | 
				
			||||||
		if regionData, err := mState.worldMap.GetJSONRegionAround(16, 16, 8); err != nil {
 | 
							if regionData, err := mState.worldMap.GetJSONRegionAround(player.X, player.Y, maxRenderDistance); err != nil {
 | 
				
			||||||
			logger.Error(err.Error())
 | 
								logger.Error(err.Error())
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,6 +116,16 @@ func (m *Match) MatchJoin(ctx context.Context, logger runtime.Logger, db *sql.DB
 | 
				
			|||||||
				logger.Error(sendErr.Error())
 | 
									logger.Error(sendErr.Error())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							for _, otherPlayer := range mState.players {
 | 
				
			||||||
 | 
								// Broadcast player data to client
 | 
				
			||||||
 | 
								if jsonObj, err := otherPlayer.GetPosJSON(); err != nil {
 | 
				
			||||||
 | 
									logger.Error(err.Error())
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if sendErr := dispatcher.BroadcastMessage(OpCodeUpdatePosition, jsonObj, []runtime.Presence{precense}, otherPlayer.Presence, true); sendErr != nil {
 | 
				
			||||||
 | 
										logger.Error(sendErr.Error())
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return mState
 | 
						return mState
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -104,20 +138,42 @@ func (m *Match) MatchLeave(ctx context.Context, logger runtime.Logger, db *sql.D
 | 
				
			|||||||
		return state
 | 
							return state
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, presence := range presences {
 | 
						for _, presence := range presences {
 | 
				
			||||||
 | 
							if _, ok := mState.presences[presence.GetUserId()]; ok {
 | 
				
			||||||
			delete(mState.presences, presence.GetUserId())
 | 
								delete(mState.presences, presence.GetUserId())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if _, ok := mState.players[presence.GetUserId()]; ok {
 | 
				
			||||||
 | 
								delete(mState.players, presence.GetUserId())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return mState
 | 
						return mState
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MatchLoop is code that is executed every tick
 | 
					// 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{} {
 | 
					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:
 | 
						mState, ok := state.(*MatchState)
 | 
				
			||||||
	// - Process the messages received.
 | 
						if !ok {
 | 
				
			||||||
	// - Update the match state based on the messages and time elapsed.
 | 
							logger.Error("Invalid match state on leave!")
 | 
				
			||||||
	// - Broadcast new data messages to match participants.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return state
 | 
							return state
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						for _, message := range messages {
 | 
				
			||||||
 | 
							if message.GetOpCode() == OpCodeUpdatePosition {
 | 
				
			||||||
 | 
								player := mState.players[message.GetUserId()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if response, err := player.ParsePositionRequest(message.GetData()); err == nil {
 | 
				
			||||||
 | 
									player.UpdateBasedOnResponse(response)
 | 
				
			||||||
 | 
									if jsonObject, err := player.GetPosJSON(); err == nil {
 | 
				
			||||||
 | 
										dispatcher.BroadcastMessage(OpCodeUpdatePosition, jsonObject, mState.GetPrecenseList(), player.Presence, false)
 | 
				
			||||||
 | 
										logger.Info("Yes")
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										logger.Error(fmt.Sprintf("Failed to get player json: %s", err.Error))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} 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
 | 
					// MatchTerminate is code that is executed when the match ends
 | 
				
			||||||
func (m *Match) MatchTerminate(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, graceSeconds int) interface{} {
 | 
					func (m *Match) MatchTerminate(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, graceSeconds int) interface{} {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										55
									
								
								server/plugin/entities/entities.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								server/plugin/entities/entities.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +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 {
 | 
				
			||||||
 | 
						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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -7,14 +7,14 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// WorldMap is the data structure used game world
 | 
					// WorldMap is the data structure used game world
 | 
				
			||||||
type WorldMap struct {
 | 
					type WorldMap struct {
 | 
				
			||||||
	data  [64][64]int
 | 
						data  [256][256]int
 | 
				
			||||||
	max_x int
 | 
						max_x int
 | 
				
			||||||
	max_y int
 | 
						max_y int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetTile method is used to grab a tile value with error checking
 | 
					// GetTile method is used to grab a tile value with error checking
 | 
				
			||||||
func (m WorldMap) GetTile(x int, y int) (int, error) {
 | 
					func (m WorldMap) GetTile(x int, y int) (int, error) {
 | 
				
			||||||
	if x > m.max_x || y > m.max_y {
 | 
						if x > m.max_x || y > m.max_y || x < 0 || y < 0 {
 | 
				
			||||||
		return -1, fmt.Errorf("Map out of bounds error: %d, %d", x, y)
 | 
							return -1, fmt.Errorf("Map out of bounds error: %d, %d", x, y)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return m.data[x][y], nil
 | 
						return m.data[x][y], nil
 | 
				
			||||||
@ -27,21 +27,22 @@ func (m WorldMap) GetJSONRegion(startX, endX, startY, endY int) ([]byte, error)
 | 
				
			|||||||
	for x := startX; x < endX; x++ {
 | 
						for x := startX; x < endX; x++ {
 | 
				
			||||||
		regionMap[x] = map[int]int{}
 | 
							regionMap[x] = map[int]int{}
 | 
				
			||||||
		for y := startY; y < endY; y++ {
 | 
							for y := startY; y < endY; y++ {
 | 
				
			||||||
			if result, err := m.GetTile(x, y); err != nil {
 | 
					
 | 
				
			||||||
				return nil, err
 | 
								// GetTile and ignore out of bounds errors
 | 
				
			||||||
			} else {
 | 
								result, _ := m.GetTile(x, y)
 | 
				
			||||||
			regionMap[x][y] = result
 | 
								regionMap[x][y] = result
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	jsonString, err := json.Marshal(regionMap)
 | 
						jsonString, err := json.Marshal(regionMap)
 | 
				
			||||||
	return jsonString, err
 | 
						return jsonString, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetJSONRegionAround returns a JSON object of tile data from a center point
 | 
					// GetJSONRegionAround returns a JSON object of tile data from a center point
 | 
				
			||||||
func (m WorldMap) GetJSONRegionAround(centerX, centerY, regionRadius int) ([]byte, error) {
 | 
					func (m WorldMap) GetJSONRegionAround(centerX float64, centerY float64, regionRadius int) ([]byte, error) {
 | 
				
			||||||
	jsonString, err := m.GetJSONRegion(centerX-regionRadius, centerX+regionRadius, centerY-regionRadius, centerY+regionRadius)
 | 
						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
 | 
						return jsonString, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,9 +50,9 @@ func (m WorldMap) GetJSONRegionAround(centerX, centerY, regionRadius int) ([]byt
 | 
				
			|||||||
// generate WorldMap objects
 | 
					// generate WorldMap objects
 | 
				
			||||||
func IntializeMap() *WorldMap {
 | 
					func IntializeMap() *WorldMap {
 | 
				
			||||||
	worldMap := new(WorldMap)
 | 
						worldMap := new(WorldMap)
 | 
				
			||||||
	worldMap.max_x = 64
 | 
						worldMap.max_x = 256
 | 
				
			||||||
	worldMap.max_y = 64
 | 
						worldMap.max_y = 256
 | 
				
			||||||
	worldMap.data = [64][64]int{}
 | 
						worldMap.data = [256][256]int{}
 | 
				
			||||||
	for x := 0; x < worldMap.max_x; x++ {
 | 
						for x := 0; x < worldMap.max_x; x++ {
 | 
				
			||||||
		for y := 0; y < worldMap.max_y; y++ {
 | 
							for y := 0; y < worldMap.max_y; y++ {
 | 
				
			||||||
			worldMap.data[x][y] = 0
 | 
								worldMap.data[x][y] = 0
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user