From cd22925449a00bcac7a1d8e1275c9ce529a7e3ed Mon Sep 17 00:00:00 2001 From: adtpdn Date: Tue, 4 Nov 2025 10:43:34 +0800 Subject: [PATCH] Refactor item grabbing logic and remove TileGrabber Moved item grabbing and auto-arrange logic from the separate TileGrabber node into the player.gd script, consolidating server/client authority checks and RPC handling for multiplayer. Removed obsolete tile_grabber.gd and its UID file. Updated main.tscn to clean up unused UI nodes and adjust visibility settings for player boards. --- scenes/main.tscn | 41 +-------- scenes/player.gd | 176 +++++++++++++++++++++++++++++++++---- scenes/tile_grabber.gd | 172 ------------------------------------ scenes/tile_grabber.gd.uid | 1 - 4 files changed, 159 insertions(+), 231 deletions(-) delete mode 100644 scenes/tile_grabber.gd delete mode 100644 scenes/tile_grabber.gd.uid diff --git a/scenes/main.tscn b/scenes/main.tscn index a506c91..493b3c1 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -74,6 +74,7 @@ grow_vertical = 2 theme_override_styles/panel = ExtResource("5_dvx6y") [node name="NetworkInfo" type="VBoxContainer" parent="NetworkPanel"] +layout_mode = 0 offset_left = 8.0 offset_right = 124.0 offset_bottom = 50.0 @@ -90,7 +91,6 @@ horizontal_alignment = 1 vertical_alignment = 1 [node name="PlayerboardUI" type="GridContainer" parent="."] -visible = false clip_contents = true anchors_preset = 2 anchor_top = 1.0 @@ -1056,44 +1056,6 @@ environment = ExtResource("4_ky38j") [node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="MarginContainer" type="Control" parent="."] -visible = false -clip_contents = true -layout_direction = 2 -layout_mode = 3 -anchors_preset = 5 -anchor_left = 0.5 -anchor_right = 0.5 -offset_left = -248.0 -offset_top = 24.0 -offset_right = 275.0 -offset_bottom = 129.0 -grow_horizontal = 2 - -[node name="Panel4" type="Panel" parent="MarginContainer"] -custom_minimum_size = Vector2(105, 105) -layout_mode = 2 -offset_left = 137.0 -offset_right = 242.0 -offset_bottom = 105.0 -theme_override_styles/panel = SubResource("StyleBoxTexture_5oeq4") - -[node name="Panel5" type="Panel" parent="MarginContainer"] -custom_minimum_size = Vector2(105, 105) -layout_mode = 2 -offset_left = 275.0 -offset_right = 380.0 -offset_bottom = 105.0 -theme_override_styles/panel = SubResource("StyleBoxTexture_5oeq4") - -[node name="Panel6" type="Panel" parent="MarginContainer"] -custom_minimum_size = Vector2(105, 105) -layout_mode = 2 -offset_left = 413.0 -offset_right = 518.0 -offset_bottom = 105.0 -theme_override_styles/panel = SubResource("StyleBoxTexture_5oeq4") - [node name="AllPlayerGoals" type="HBoxContainer" parent="."] y_sort_enabled = true clip_contents = true @@ -6052,6 +6014,7 @@ grow_vertical = 2 texture = ExtResource("10_my1qp") [node name="AllPlayerBoards" type="TabContainer" parent="."] +visible = false clip_contents = true anchors_preset = 4 anchor_top = 0.5 diff --git a/scenes/player.gd b/scenes/player.gd index a350f47..7c7e0d2 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -947,6 +947,9 @@ func bot_try_grab_item() -> bool: return false +# ----------------------------------------------------------------- +# OLD GRAB Func +# ----------------------------------------------------------------- #func grab_item(grid_position: Vector2i = current_position) -> bool: #if is_bot: #return bot_try_grab_item() @@ -995,15 +998,60 @@ func bot_try_grab_item() -> bool: #return true # #return false - -func grab_item(grid_position: Vector2i = current_position) -> bool: +# ----------------------------------------------------------------- +#func grab_item(grid_position: Vector2i = current_position) -> bool: + #if not enhanced_gridmap or action_points <= 0: + #return false +# + #var cell = Vector3i(grid_position.x, 1, grid_position.y) + #var item = enhanced_gridmap.get_cell_item(cell) +# + ## Validate adjacency (unless it's current position) + #if grid_position != current_position: + #var neighbors = enhanced_gridmap.get_neighbors(current_position, 0) + #var is_adjacent = false + #for neighbor in neighbors: + #if neighbor.position == grid_position: + #is_adjacent = true + #break + #if not is_adjacent: + #return false +# + #if item == -1: + #return false +# + ## === AUTO-ARRANGE LOGIC === + #var target_slot = find_best_goal_slot_for_item(item) + #if target_slot == -1: + #return false # no space +# + ## Perform the grab and auto-place + #if is_multiplayer_authority(): + ## Update gridmap: remove item + #rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) + ## Update playerboard + #playerboard[target_slot] = item + #rpc("sync_playerboard", playerboard) + ## Consume action + #has_performed_action = true + #consume_action_points(1) +# + ## Optional: visual feedback + #var main = get_tree().get_root().get_node_or_null("Main") + #if main: + #main.update_playerboard_ui() + #main.set_action_state(main.ActionState.NONE) +# + #return true +# ----------------------------------------------------------------- +func grab_item(grid_position: Vector2i = current_position) -> bool: if not enhanced_gridmap or action_points <= 0: return false var cell = Vector3i(grid_position.x, 1, grid_position.y) var item = enhanced_gridmap.get_cell_item(cell) - # Validate adjacency (unless it's current position) + # Validate adjacency (unless it's current position) if grid_position != current_position: var neighbors = enhanced_gridmap.get_neighbors(current_position, 0) var is_adjacent = false @@ -1017,32 +1065,108 @@ func grab_item(grid_position: Vector2i = current_position) -> bool: if item == -1: return false - # === AUTO-ARRANGE LOGIC === + # === AUTO-ARRANGE LOGIC (Client-side pre-check) === var target_slot = find_best_goal_slot_for_item(item) if target_slot == -1: + print("Player: No valid slot found for item.") return false # no space - # Perform the grab and auto-place - if is_multiplayer_authority(): - # Update gridmap: remove item - rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) - # Update playerboard - playerboard[target_slot] = item - rpc("sync_playerboard", playerboard) - # Consume action - has_performed_action = true - consume_action_points(1) + if not is_multiplayer_authority(): + return false - # Optional: visual feedback - var main = get_tree().get_root().get_node_or_null("Main") - if main: - main.update_playerboard_ui() - main.set_action_state(main.ActionState.NONE) + # === Branching Logic: Host vs Client === + if multiplayer.is_server(): + # HOST/SERVER: Call the logic directly + _execute_grab(grid_position, cell, item) + else: + # CLIENT: Send RPC request to server (peer 1) + rpc_id(1, "request_server_grab", grid_position, cell.x, cell.y, cell.z, item) + + return true # Request was sent or processed +# ----------------------------------------------------------------- +# Execute Grab +# ----------------------------------------------------------------- + +func _execute_grab(grid_pos: Vector2i, cell: Vector3i, item_id: int): + var main = get_tree().get_root().get_node_or_null("Main") + if not main: + push_error("Server: Main node not found.") + return false + + var server_gridmap = main.get_node("EnhancedGridMap") + if not server_gridmap: + push_error("Server: EnhancedGridMap not found.") + return false + + # 1. Server-side Validation + var server_item = server_gridmap.get_cell_item(cell) + + # Check if item is still there + if server_item != item_id: + print("Server: Item mismatch or already taken. Server has ", server_item) + return false + + # Check action points + if action_points <= 0: + print("Server: Player has no action points.") + return false + + # Check adjacency + if grid_pos != current_position: + var neighbors = server_gridmap.get_neighbors(current_position, 0) + if not neighbors.any(func(n): return n.position == grid_pos): + print("Server: Player is not adjacent to item.") + return false + + # 2. Server-side Auto-Arrange + var target_slot = find_best_goal_slot_for_item(item_id) + if target_slot == -1: + print("Server: Player has no valid slot for item.") + return false + + # 3. Server Executes the Action + + # 3a. Update gridmap (using Main's RPC, which has authority) + main.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) + + # 3b. Update playerboard state (on this server-side instance) + playerboard[target_slot] = item_id + + # 3c. Broadcast the new playerboard state to all clients + rpc("sync_playerboard", playerboard) + + # 3d. Consume action points + has_performed_action = true + consume_action_points(1) + + # 3e. Reset the UI for the player who acted + # This will RPC to the client, or run locally for the host + rpc("force_action_state_none") + return true +# ----------------------------------------------------------------- +# This function runs on the server when requested by a client +# ----------------------------------------------------------------- +@rpc("any_peer", "reliable") +func request_server_grab(grid_pos: Vector2i, x: int, y: int, z: int, item_id: int): + # 1. Only the server (peer 1) should process this + if not multiplayer.is_server(): + return + + # 2. Security check: Did this request come from the actual owner of this node? + if multiplayer.get_remote_sender_id() != get_multiplayer_authority(): + push_error("Security: Non-authority tried to grab item!") + return + + # 3. Call the execution logic + _execute_grab(grid_pos, Vector3i(x, y, z), item_id) + +# ----------------------------------------------------------------- # Auto-put: no manual selection needed # Automatically puts a goal-matching tile into an adjacent (or current) empty grid cell +# ----------------------------------------------------------------- func auto_put_item() -> bool: if not enhanced_gridmap or action_points <= 0 or is_bot or is_in_group("Bots"): return false @@ -1134,6 +1258,20 @@ func auto_put_item() -> bool: return true +# ----------------------------------------------------------------- +# Force ActionState : None +# ----------------------------------------------------------------- + +@rpc("authority", "reliable") +func force_action_state_none(): + # This is called by the server on the client to reset the UI + var main = get_tree().get_root().get_node_or_null("Main") + if main: + main.set_action_state(main.ActionState.NONE) + clear_highlights() + clear_playerboard_highlights() + +# ----------------------------------------------------------------- @rpc("any_peer", "reliable") func request_server_put(grid_position: Vector2i, slot_index: int, x: int, y: int, z: int, item: int): diff --git a/scenes/tile_grabber.gd b/scenes/tile_grabber.gd deleted file mode 100644 index 1edb927..0000000 --- a/scenes/tile_grabber.gd +++ /dev/null @@ -1,172 +0,0 @@ -# tekton-enet/scripts/tile_grabber.gd -extends Node - -class_name TileGrabber - -signal tile_grabbed(tile_id: int, position: Vector3i) -signal tiles_arranged(arranged_tiles: Array) - -@export var enhanced_gridmap_path: NodePath -@export var player_board_path: NodePath - -var enhanced_gridmap: EnhancedGridMap -var player_board: Node -var grabbed_tile: int = -1 -var grabbed_position: Vector3i - -# Goal combinations - define what patterns score points -var goal_combinations = [ - { - "name": "Line of 3", - "pattern": [ - [1, 1, 1], - [0, 0, 0], - [0, 0, 0] - ], - "score": 10 - }, - { - "name": "L-Shape", - "pattern": [ - [1, 0, 0], - [1, 0, 0], - [1, 1, 0] - ], - "score": 15 - }, - { - "name": "Square", - "pattern": [ - [1, 1, 0], - [1, 1, 0], - [0, 0, 0] - ], - "score": 20 - } -] - -func _ready(): - if enhanced_gridmap_path: - enhanced_gridmap = get_node(enhanced_gridmap_path) - if player_board_path: - player_board = get_node(player_board_path) - -func grab_tile(grid_position: Vector3i) -> bool: - if not enhanced_gridmap: - print("Error: EnhancedGridMap not found") - return false - - # Get the tile at the specified position - var tile_id = enhanced_gridmap.get_cell_item(grid_position) - if tile_id == -1: - print("No tile at position: ", grid_position) - return false - - # Store the grabbed tile and its position - grabbed_tile = tile_id - grabbed_position = grid_position - - # Remove the tile from the grid - enhanced_gridmap.set_cell_item(grid_position, -1) - - # Emit signal that a tile was grabbed - tile_grabbed.emit(tile_id, grid_position) - - print("Grabbed tile ", tile_id, " from position ", grid_position) - return true - -func auto_arrange_tile() -> bool: - if grabbed_tile == -1: - print("No tile to arrange") - return false - - # Find the best position for the grabbed tile based on goal combinations - var best_position = find_best_position_for_tile(grabbed_tile) - - if best_position == Vector3i(-1, -1, -1): - print("No valid position found for tile") - return false - - # Place the tile at the best position - enhanced_gridmap.set_cell_item(best_position, grabbed_tile) - - # Reset grabbed tile - grabbed_tile = -1 - grabbed_position = Vector3i(-1, -1, -1) - - # Emit signal that tiles were arranged - tiles_arranged.emit([best_position]) - - print("Auto-arranged tile at position: ", best_position) - return true - -func find_best_position_for_tile(tile_id: int) -> Vector3i: - var best_position = Vector3i(-1, -1, -1) - var best_score = -1 - - # Check all possible positions on the grid - for x in range(enhanced_gridmap.columns): - for z in range(enhanced_gridmap.rows): - var position = Vector3i(x, 0, z) - - # Skip if position is already occupied - if enhanced_gridmap.get_cell_item(position) != -1: - continue - - # Temporarily place the tile to check for goal combinations - enhanced_gridmap.set_cell_item(position, tile_id) - - # Calculate score for this position - var score = calculate_position_score(position) - - # Remove the temporary tile - enhanced_gridmap.set_cell_item(position, -1) - - # Update best position if this one scores higher - if score > best_score: - best_score = score - best_position = position - - return best_position - -func calculate_position_score(position: Vector3i) -> int: - var total_score = 0 - - # Check each goal combination - for goal in goal_combinations: - var matches = check_goal_combination(position, goal.pattern) - if matches: - total_score += goal.score - - return total_score - -func check_goal_combination(center_position: Vector3i, pattern: Array) -> bool: - # Get the pattern dimensions - var pattern_width = pattern[0].size() - var pattern_height = pattern.size() - - # Calculate the top-left position of the pattern - var start_x = center_position.x - (pattern_width / 2) - var start_z = center_position.z - (pattern_height / 2) - - # Check if the pattern fits within the grid - if start_x < 0 or start_z < 0 or start_x + pattern_width > enhanced_gridmap.columns or start_z + pattern_height > enhanced_gridmap.rows: - return false - - # Check each cell in the pattern - for z in range(pattern_height): - for x in range(pattern_width): - var pattern_value = pattern[z][x] - var grid_position = Vector3i(start_x + x, 0, start_z + z) - var grid_value = enhanced_gridmap.get_cell_item(grid_position) - - # If pattern expects a tile (1) but grid is empty (-1), no match - if pattern_value == 1 and grid_value == -1: - return false - - # If pattern expects empty (0) but grid has a tile, no match - if pattern_value == 0 and grid_value != -1: - return false - - # All cells match the pattern - return true diff --git a/scenes/tile_grabber.gd.uid b/scenes/tile_grabber.gd.uid deleted file mode 100644 index f212b64..0000000 --- a/scenes/tile_grabber.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dc1cw21830bfp