feat: directly retrieve files to restore missing functions from commit
This commit is contained in:
@@ -22,8 +22,8 @@ var auth_mode: AuthMode = AuthMode.GUEST
|
||||
const SESSION_FILE := "user://auth_session.dat"
|
||||
const CREDENTIALS_FILE := "user://auth_credentials.dat"
|
||||
|
||||
# Encryption key for session storage (replace with your own!)
|
||||
const ENCRYPTION_KEY := "tekton_secret_key_change_me_123"
|
||||
# Encryption key for session storage (device-specific)
|
||||
var ENCRYPTION_KEY: String = OS.get_unique_id().sha256_text()
|
||||
|
||||
func _ready() -> void:
|
||||
# Try to restore session on startup
|
||||
@@ -40,8 +40,7 @@ func _try_restore_session() -> void:
|
||||
|
||||
var file := FileAccess.open_encrypted_with_pass(SESSION_FILE, FileAccess.READ, ENCRYPTION_KEY)
|
||||
if not file:
|
||||
print("[AuthManager] Could not open session file. Corrupt or wrong key. Deleting.")
|
||||
DirAccess.remove_absolute(SESSION_FILE)
|
||||
print("[AuthManager] Could not open session file")
|
||||
return
|
||||
|
||||
var session_data = file.get_var()
|
||||
@@ -369,7 +368,7 @@ func login_with_steam() -> bool:
|
||||
return true
|
||||
|
||||
func _authenticate_steam_with_fallback(steamworks: Node) -> NakamaSession:
|
||||
# Try proper Steam ticket auth first
|
||||
# Proper Steam ticket auth
|
||||
var auth_ticket = steamworks.get_auth_session_ticket()
|
||||
if not auth_ticket.is_empty():
|
||||
print("[AuthManager] Got Steam auth ticket, authenticating with Nakama...")
|
||||
@@ -377,22 +376,10 @@ func _authenticate_steam_with_fallback(steamworks: Node) -> NakamaSession:
|
||||
if not session.is_exception():
|
||||
return session
|
||||
print("[AuthManager] Steam ticket auth failed: %s" % session.get_exception().message)
|
||||
print("[AuthManager] Falling back to Steam ID custom auth (dev mode)...")
|
||||
|
||||
# Fallback: use Steam ID + username to create an email-style account (works without publisher key)
|
||||
var steam_id = str(steamworks.get_steam_user_id())
|
||||
var steam_name = steamworks.get_steam_user_name()
|
||||
if steam_id == "0" or steam_id.is_empty():
|
||||
return null
|
||||
|
||||
# Derive email and password from Steam credentials
|
||||
var email = steam_name.to_lower().replace(" ", "_") + "@steam.local"
|
||||
var password = steam_name # Default password = Steam username
|
||||
var username = steam_name
|
||||
|
||||
print("[AuthManager] Using Steam email auth: %s (%s)" % [email, username])
|
||||
var fallback_session: NakamaSession = await NakamaManager.client.authenticate_email_async(email, password, username, true)
|
||||
return fallback_session
|
||||
print("[AuthManager] Steam auth ticket is empty.")
|
||||
return null
|
||||
|
||||
# =============================================================================
|
||||
# Account Linking (Convert Guest to Full Account)
|
||||
@@ -455,28 +442,44 @@ func logout() -> void:
|
||||
# =============================================================================
|
||||
|
||||
func _connect_socket() -> bool:
|
||||
if not NakamaManager.session:
|
||||
push_error("[AuthManager] Socket connection failed: no Nakama session")
|
||||
return false
|
||||
|
||||
if NakamaManager.socket and NakamaManager.socket.is_connected_to_host():
|
||||
if not multiplayer.has_multiplayer_peer() and NakamaManager.bridge:
|
||||
multiplayer.set_multiplayer_peer(NakamaManager.bridge.multiplayer_peer)
|
||||
NakamaManager.connected_to_nakama.emit()
|
||||
return true
|
||||
|
||||
|
||||
if NakamaManager.socket:
|
||||
NakamaManager.socket.close()
|
||||
NakamaManager.socket = null
|
||||
|
||||
NakamaManager.socket = Nakama.create_socket_from(NakamaManager.client)
|
||||
var result = await NakamaManager.socket.connect_async(NakamaManager.session)
|
||||
|
||||
|
||||
if result.is_exception():
|
||||
push_error("[AuthManager] Socket connection failed: " + result.get_exception().message)
|
||||
var exception = result.get_exception()
|
||||
var error_message = "Socket connection failed"
|
||||
if exception and not exception.message.is_empty():
|
||||
error_message = exception.message
|
||||
elif exception and exception.status_code >= 0:
|
||||
error_message = "Socket connection failed with error code %s" % exception.status_code
|
||||
push_error("[AuthManager] " + error_message)
|
||||
NakamaManager.socket.close()
|
||||
NakamaManager.socket = null
|
||||
return false
|
||||
|
||||
|
||||
# Initialize multiplayer bridge
|
||||
NakamaManager.bridge = NakamaMultiplayerBridge.new(NakamaManager.socket)
|
||||
NakamaManager.bridge.match_joined.connect(NakamaManager._on_bridge_match_joined)
|
||||
NakamaManager.bridge.match_join_error.connect(NakamaManager._on_bridge_match_join_error)
|
||||
multiplayer.set_multiplayer_peer(NakamaManager.bridge.multiplayer_peer)
|
||||
|
||||
|
||||
# Notify other systems that Nakama socket is ready
|
||||
NakamaManager.connected_to_nakama.emit()
|
||||
|
||||
|
||||
return true
|
||||
|
||||
func _load_user_profile() -> void:
|
||||
|
||||
@@ -31,6 +31,11 @@ signal doors_swap_time_changed(time: int)
|
||||
signal doors_refresh_time_changed(time: int)
|
||||
signal doors_required_goals_changed(goals: int)
|
||||
|
||||
# Gauntlet settings signals
|
||||
signal gauntlet_round_duration_changed(duration: int)
|
||||
signal gauntlet_cannon_interval_changed(interval: int)
|
||||
signal gauntlet_volley_size_changed(size: int)
|
||||
|
||||
# Room data structure
|
||||
var current_room: Dictionary = {}
|
||||
var players_in_room: Array = [] # [{id, name, is_ready}]
|
||||
@@ -74,13 +79,18 @@ var doors_swap_time: int = 15
|
||||
var doors_refresh_time: int = 25
|
||||
var doors_required_goals: int = 8
|
||||
|
||||
# Gauntlet settings
|
||||
var gauntlet_round_duration: int = 180
|
||||
var gauntlet_cannon_interval: int = 5
|
||||
var gauntlet_volley_size: int = 5
|
||||
|
||||
# Rematch tracking
|
||||
var rematch_votes: Array = [] # [player_id, ...]
|
||||
|
||||
# Character and area selection
|
||||
var available_characters: Array[String] = ["Copper", "Dabro", "Gatot", "Pip", "Random"]
|
||||
var available_areas: Array[String] = []
|
||||
var available_game_modes: Array[String] = ["Freemode", "Stop n Go"]
|
||||
var available_game_modes: Array[String] = ["Freemode", "Stop n Go", "Candy Cannon Survival"]
|
||||
var selected_area: String = "Freemode Arena" # Host-controlled
|
||||
var game_mode: String = "Freemode" # Host-controlled
|
||||
var local_character_index: int = 0 # Local player's character index
|
||||
@@ -135,6 +145,8 @@ func _update_available_areas(mode: String) -> void:
|
||||
available_areas = ["Freemode Arena", "Classic", "Colloseum"]
|
||||
"Stop n Go":
|
||||
available_areas = ["Stop N Go Arena"]
|
||||
"Candy Cannon Survival":
|
||||
available_areas = ["Gauntlet Arena"]
|
||||
_:
|
||||
available_areas = ["Classic"]
|
||||
|
||||
@@ -537,6 +549,37 @@ func sync_doors_required_goals(goals: int) -> void:
|
||||
doors_required_goals = goals
|
||||
emit_signal("doors_required_goals_changed", goals)
|
||||
|
||||
# =============================================================================
|
||||
# Gauntlet Settings
|
||||
# =============================================================================
|
||||
|
||||
func set_gauntlet_round_duration(duration: int) -> void:
|
||||
gauntlet_round_duration = duration
|
||||
if is_host: rpc("sync_gauntlet_round_duration", duration)
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func sync_gauntlet_round_duration(duration: int) -> void:
|
||||
gauntlet_round_duration = duration
|
||||
emit_signal("gauntlet_round_duration_changed", duration)
|
||||
|
||||
func set_gauntlet_cannon_interval(interval: int) -> void:
|
||||
gauntlet_cannon_interval = interval
|
||||
if is_host: rpc("sync_gauntlet_cannon_interval", interval)
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func sync_gauntlet_cannon_interval(interval: int) -> void:
|
||||
gauntlet_cannon_interval = interval
|
||||
emit_signal("gauntlet_cannon_interval_changed", interval)
|
||||
|
||||
func set_gauntlet_volley_size(size: int) -> void:
|
||||
gauntlet_volley_size = size
|
||||
if is_host: rpc("sync_gauntlet_volley_size", size)
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func sync_gauntlet_volley_size(size: int) -> void:
|
||||
gauntlet_volley_size = size
|
||||
emit_signal("gauntlet_volley_size_changed", size)
|
||||
|
||||
# =============================================================================
|
||||
# Character Selection
|
||||
# =============================================================================
|
||||
@@ -682,14 +725,40 @@ func set_game_mode(mode: String) -> void:
|
||||
rpc("sync_game_mode", mode)
|
||||
|
||||
_update_available_areas(mode)
|
||||
|
||||
# Only force switch the area if the selected area is NOT valid for this mode
|
||||
if selected_area not in available_areas:
|
||||
set_area(available_areas[0])
|
||||
else:
|
||||
# Important: even if the area is technically in the list, if they just clicked Free Mode
|
||||
# we should default them to Free Mode Area if they were on Stop n Go Area before.
|
||||
if mode == "Free Mode" and "Free Mode Area" in available_areas:
|
||||
set_area("Free Mode Area")
|
||||
elif mode == "Stop n Go" and "Stop n Go Area" in available_areas:
|
||||
set_area("Stop n Go Area")
|
||||
elif mode == "Tekton Doors" and "Tekton Doors Area" in available_areas:
|
||||
set_area("Tekton Doors Area")
|
||||
elif mode == "Gauntlet" and "Candy Pump Arena" in available_areas:
|
||||
set_area("Candy Pump Arena")
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func sync_game_mode(mode: String) -> void:
|
||||
"""Sync game mode selection from host to clients."""
|
||||
game_mode = mode
|
||||
_update_available_areas(mode)
|
||||
|
||||
# Try to smart-match the client's local area to the mode as well so their UI matches
|
||||
if mode == "Free Mode" and "Free Mode Area" in available_areas:
|
||||
selected_area = "Free Mode Area"
|
||||
elif mode == "Stop n Go" and "Stop n Go Area" in available_areas:
|
||||
selected_area = "Stop n Go Area"
|
||||
elif mode == "Tekton Doors" and "Tekton Doors Area" in available_areas:
|
||||
selected_area = "Tekton Doors Area"
|
||||
elif mode == "Gauntlet" and "Candy Pump Arena" in available_areas:
|
||||
selected_area = "Candy Pump Arena"
|
||||
elif selected_area not in available_areas:
|
||||
selected_area = available_areas[0]
|
||||
|
||||
emit_signal("game_mode_changed", mode)
|
||||
|
||||
func start_game(force: bool = false) -> void:
|
||||
@@ -715,6 +784,10 @@ func start_game(force: bool = false) -> void:
|
||||
rpc("sync_doors_swap_time", doors_swap_time)
|
||||
rpc("sync_doors_refresh_time", doors_refresh_time)
|
||||
rpc("sync_doors_required_goals", doors_required_goals)
|
||||
# Sync gauntlet settings
|
||||
rpc("sync_gauntlet_round_duration", gauntlet_round_duration)
|
||||
rpc("sync_gauntlet_cannon_interval", gauntlet_cannon_interval)
|
||||
rpc("sync_gauntlet_volley_size", gauntlet_volley_size)
|
||||
# Sync game mode
|
||||
rpc("sync_game_mode", game_mode)
|
||||
|
||||
@@ -790,6 +863,9 @@ func request_room_info(requester_id: int, requester_name: String, requester_char
|
||||
rpc_id(requester_id, "sync_doors_swap_time", doors_swap_time)
|
||||
rpc_id(requester_id, "sync_doors_refresh_time", doors_refresh_time)
|
||||
rpc_id(requester_id, "sync_doors_required_goals", doors_required_goals)
|
||||
rpc_id(requester_id, "sync_gauntlet_round_duration", gauntlet_round_duration)
|
||||
rpc_id(requester_id, "sync_gauntlet_cannon_interval", gauntlet_cannon_interval)
|
||||
rpc_id(requester_id, "sync_gauntlet_volley_size", gauntlet_volley_size)
|
||||
rpc_id(requester_id, "sync_game_mode", game_mode)
|
||||
rpc_id(requester_id, "sync_area", selected_area)
|
||||
|
||||
|
||||
@@ -289,7 +289,9 @@ func get_action_display(action_key: String) -> String:
|
||||
"grab": "ctrl_grab",
|
||||
"use_powerup": "ctrl_use_powerup",
|
||||
"tekton_grab": "ctrl_tekton_grab",
|
||||
"action_grab_tekton": "ctrl_tekton_grab",
|
||||
"attack_mode": "ctrl_attack_mode",
|
||||
"action_knock_tekton": "ctrl_attack_mode",
|
||||
}
|
||||
if ctrl_key_map.has(action_key):
|
||||
return get_controller_binding_text(ctrl_key_map[action_key])
|
||||
@@ -315,7 +317,16 @@ func is_controller_button_used(button_index: int) -> String:
|
||||
return ""
|
||||
|
||||
func get_control_keycode(action_name: String) -> int:
|
||||
return settings.controls.get(action_name, -1)
|
||||
# Map friendly names to their internal settings.controls keys
|
||||
var mapped_name = action_name
|
||||
if action_name == "tekton_grab":
|
||||
mapped_name = "action_grab_tekton"
|
||||
elif action_name == "attack_mode":
|
||||
mapped_name = "action_knock_tekton"
|
||||
elif action_name == "grab":
|
||||
mapped_name = "action_grab"
|
||||
|
||||
return settings.controls.get(mapped_name, -1)
|
||||
|
||||
func get_control_text(action_name: String) -> String:
|
||||
var code = get_control_keycode(action_name)
|
||||
|
||||
+22
-19
@@ -1,11 +1,10 @@
|
||||
extends Node
|
||||
|
||||
# Standard Nakama Configuration
|
||||
var nakama_server_key = "defaultkey"
|
||||
var nakama_host = "thunderobot.tapir-atria.ts.net"
|
||||
var nakama_port = 7350
|
||||
var nakama_scheme = "https"
|
||||
var nakama_port_funnel = 443
|
||||
var nakama_server_key = OS.get_environment("NAKAMA_SERVER_KEY") if OS.has_environment("NAKAMA_SERVER_KEY") else ProjectSettings.get_setting("network/nakama/server_key", "defaultkey")
|
||||
var nakama_host = OS.get_environment("NAKAMA_HOST") if OS.has_environment("NAKAMA_HOST") else ProjectSettings.get_setting("network/nakama/host", "tektondash.vps.webdock.cloud")
|
||||
var nakama_port = OS.get_environment("NAKAMA_PORT").to_int() if OS.has_environment("NAKAMA_PORT") else ProjectSettings.get_setting("network/nakama/port", 7350)
|
||||
var nakama_scheme = OS.get_environment("NAKAMA_SCHEME") if OS.has_environment("NAKAMA_SCHEME") else ProjectSettings.get_setting("network/nakama/scheme", "http")
|
||||
|
||||
# Core Nakama Variables
|
||||
var client: NakamaClient
|
||||
@@ -30,10 +29,7 @@ func _ready():
|
||||
set_process(true)
|
||||
|
||||
func _init_client():
|
||||
if nakama_host == "thunderobot.tapir-atria.ts.net":
|
||||
client = Nakama.create_client(nakama_server_key, nakama_host, 443, "https")
|
||||
else:
|
||||
client = Nakama.create_client(nakama_server_key, nakama_host, nakama_port, nakama_scheme)
|
||||
client = Nakama.create_client(nakama_server_key, nakama_host, nakama_port, nakama_scheme)
|
||||
|
||||
func set_server(host: String, port: int = 7350):
|
||||
# Clean up the host string
|
||||
@@ -127,8 +123,16 @@ func connect_to_nakama_async(email: String = "", password: String = "") -> bool:
|
||||
return false
|
||||
elif socket_result.is_exception():
|
||||
var err = socket_result.get_exception()
|
||||
printerr("[NakamaManager] Socket Error: %s (Code: %s)" % [err.message, err.status_code])
|
||||
emit_signal("connection_failed", err.message)
|
||||
var err_msg = "Socket connection failed"
|
||||
if err and not err.message.is_empty():
|
||||
err_msg = err.message
|
||||
elif err and err.status_code >= 0:
|
||||
err_msg = "Socket connection failed with code %s" % err.status_code
|
||||
printerr("[NakamaManager] Socket Error: %s (Code: %s)" % [err_msg, err.status_code if err else -1])
|
||||
emit_signal("connection_failed", err_msg)
|
||||
if socket:
|
||||
socket.close()
|
||||
socket = null
|
||||
return false
|
||||
|
||||
# 3. Initialize Multiplayer Bridge
|
||||
@@ -184,8 +188,10 @@ func host_game(room_meta: Dictionary = {}):
|
||||
printerr("Cannot host: Bridge not initialized")
|
||||
return
|
||||
print("Hosting match via Nakama Bridge...")
|
||||
await bridge.create_match()
|
||||
# Errors are emitted via bridge's match_join_error signal
|
||||
var result = await bridge.create_match()
|
||||
if result and result.is_exception():
|
||||
emit_signal("match_join_error", result.get_exception().message)
|
||||
return
|
||||
# Store room metadata in Nakama storage so other players can see it in listings
|
||||
if session and current_match_id and room_meta.size() > 0:
|
||||
var meta_json = JSON.stringify(room_meta)
|
||||
@@ -211,13 +217,10 @@ func join_game(match_id: String):
|
||||
# Wait a bit for cleanup
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
|
||||
if not bridge:
|
||||
printerr("Cannot join: Bridge became null while waiting")
|
||||
return
|
||||
|
||||
print("Joining match: ", match_id)
|
||||
await bridge.join_match(match_id)
|
||||
# Errors are emitted via bridge's match_join_error signal
|
||||
var result = await bridge.join_match(match_id)
|
||||
if result and result.is_exception():
|
||||
emit_signal("match_join_error", result.get_exception().message)
|
||||
|
||||
# --- Callbacks ---
|
||||
|
||||
|
||||
+19
-1
@@ -414,7 +414,16 @@ func spawn_tiles_around(count: int = 4):
|
||||
# FIX 1: Make tekton look/rotate toward a random spawning direction
|
||||
if not is_carried and not is_thrown:
|
||||
var random_angle = rng.randf_range(0, TAU)
|
||||
rotation.y = random_angle
|
||||
|
||||
# If it's a static turret, make it face the target tile it's about to spawn instead
|
||||
if is_static_turret:
|
||||
# We don't have a specific target yet, but we can pick an average direction
|
||||
# Or just let it throw randomly like the others. Wait, the user wants:
|
||||
# "static tekton, should facing toward where they're going to thrown the tiles"
|
||||
# We'll calculate rotation inside the spawning loop for static turrets.
|
||||
pass
|
||||
else:
|
||||
rotation.y = random_angle
|
||||
|
||||
# Play throw animation
|
||||
if not is_carried and not is_thrown:
|
||||
@@ -439,6 +448,12 @@ func spawn_tiles_around(count: int = 4):
|
||||
|
||||
var pos = current_position + Vector2i(x, y)
|
||||
|
||||
# For static turret, update rotation to face the exact tile being thrown
|
||||
if is_static_turret and not is_carried and not is_thrown:
|
||||
var throw_dir = Vector3(x, 0, y).normalized()
|
||||
if throw_dir.length_squared() > 0.01:
|
||||
rotation.y = atan2(throw_dir.x, throw_dir.z)
|
||||
|
||||
# Don't overwrite the Tekton's own cell? Or do?
|
||||
# Maybe avoid center.
|
||||
if x == 0 and y == 0: continue
|
||||
@@ -472,6 +487,9 @@ func spawn_tiles_around(count: int = 4):
|
||||
if roll < 0.6 or (LobbyManager and LobbyManager.game_mode == "Stop n Go"):
|
||||
# 60% Normal Tile (7-10) OR 100% if Stop n Go (User Request)
|
||||
item_id = rng.randi_range(7, 10)
|
||||
elif LobbyManager and LobbyManager.get_game_mode() == GameMode.Mode.GAUNTLET:
|
||||
# Gauntlet mode: No power-up spawns from Tekton grabs
|
||||
item_id = rng.randi_range(7, 10)
|
||||
else:
|
||||
# 40% PowerUp (11-14)
|
||||
var mode = GameMode.Mode.FREEMODE
|
||||
|
||||
Reference in New Issue
Block a user