From a0791d599c5ee2983ff85516dda70576bc2d9fc2 Mon Sep 17 00:00:00 2001 From: smaubio <118258478+smaubio@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:04:53 +0100 Subject: [PATCH] Add player nickname feature to game registration --- crypto_clash_contract/contracts/Game.sol | 7 +- crypto_clash_frontend/app/clash/Commit.tsx | 21 ++++ crypto_clash_frontend/app/clash/GameList.tsx | 112 +++++++++++++++---- crypto_clash_frontend/app/clash/Reveal.tsx | 20 ++++ 4 files changed, 135 insertions(+), 25 deletions(-) diff --git a/crypto_clash_contract/contracts/Game.sol b/crypto_clash_contract/contracts/Game.sol index 8329f40..7e74e04 100644 --- a/crypto_clash_contract/contracts/Game.sol +++ b/crypto_clash_contract/contracts/Game.sol @@ -67,7 +67,8 @@ contract Game { // If gameId is 0, player will join or create the first available game. // Return player's ID and game ID upon successful registration. function register( - uint gameId + uint gameId, + string memory nickname ) public payable @@ -80,11 +81,14 @@ contract Game { } require(games[gameId].isActive, "Game is not active"); + require(bytes(nickname).length > 0, "Nickname cannot be empty"); + require(bytes(nickname).length <= 20, "Nickname too long (max 20 characters)"); GameState storage game = games[gameId]; if (game.playerA.addr == address(0x0)) { game.playerA.addr = payable(msg.sender); + game.playerA.nickname = nickname; game.initialBet = msg.value; return (1, gameId); } else if (game.playerB.addr == address(0x0)) { @@ -93,6 +97,7 @@ contract Game { "Cannot play against yourself" ); game.playerB.addr = payable(msg.sender); + game.playerB.nickname = nickname; return (2, gameId); } diff --git a/crypto_clash_frontend/app/clash/Commit.tsx b/crypto_clash_frontend/app/clash/Commit.tsx index 4ee3215..32f4304 100644 --- a/crypto_clash_frontend/app/clash/Commit.tsx +++ b/crypto_clash_frontend/app/clash/Commit.tsx @@ -226,6 +226,27 @@ export default function Commit({ return (
+ {/* Player Info */} + {gameDetails && ( +
+
+
+

You

+

+ {whoAmI === "player1" ? gameDetails.playerA.nickname : gameDetails.playerB.nickname} +

+
+
VS
+
+

Opponent

+

+ {whoAmI === "player1" ? gameDetails.playerB.nickname || "Waiting..." : gameDetails.playerA.nickname} +

+
+
+
+ )} + {/* Show timeout result after game is inactive */} {isGameFinishedByTimeout ? (
diff --git a/crypto_clash_frontend/app/clash/GameList.tsx b/crypto_clash_frontend/app/clash/GameList.tsx index 67d953a..a4e1531 100644 --- a/crypto_clash_frontend/app/clash/GameList.tsx +++ b/crypto_clash_frontend/app/clash/GameList.tsx @@ -25,6 +25,8 @@ export default function GameList({ const [games, setGames] = useState([]); const [loading, setLoading] = useState(false); const [newGameBet, setNewGameBet] = useState("0.01"); + const [newGameNickname, setNewGameNickname] = useState(""); + const [joinNicknames, setJoinNicknames] = useState>(new Map()); const [refreshInterval, setRefreshInterval] = useState(null); const [userGameIds, setUserGameIds] = useState>(new Set()); @@ -75,10 +77,21 @@ export default function GameList({ // Join an existing game const handleJoinGame = async (gameId: number, bet: string) => { if (!contract || !web3 || !account) return; + + const nickname = joinNicknames.get(gameId) || ""; + if (!nickname.trim()) { + showErrorToast("Please enter a nickname"); + return; + } + if (nickname.length > 20) { + showErrorToast("Nickname too long (max 20 characters)"); + return; + } + setLoading(true); try { const betWei = bet; - const tx = contract.methods.register(gameId); + const tx = contract.methods.register(gameId, nickname); const gas = await tx.estimateGas({ from: account, value: betWei }); const result = await (globalThis as any).ethereum.request({ method: "eth_sendTransaction", @@ -94,6 +107,10 @@ export default function GameList({ ], }); showSuccessToast("Joined game! Transaction: " + result); + // Clear the nickname input for this game + const updatedNicknames = new Map(joinNicknames); + updatedNicknames.delete(gameId); + setJoinNicknames(updatedNicknames); await new Promise((resolve) => setTimeout(resolve, 2000)); fetchActiveGames(); } catch (err: any) { @@ -107,10 +124,20 @@ export default function GameList({ // Create a new game const handleCreateGame = async () => { if (!contract || !web3 || !account) return; + + if (!newGameNickname.trim()) { + showErrorToast("Please enter a nickname"); + return; + } + if (newGameNickname.length > 20) { + showErrorToast("Nickname too long (max 20 characters)"); + return; + } + setLoading(true); try { const betWei = web3.utils.toWei(newGameBet || "0.01", "ether"); - const tx = contract.methods.register(0); // 0 means create new game + const tx = contract.methods.register(0, newGameNickname); // 0 means create new game const gas = await tx.estimateGas({ from: account, value: betWei }); const result = await (globalThis as any).ethereum.request({ method: "eth_sendTransaction", @@ -127,6 +154,7 @@ export default function GameList({ }); showSuccessToast("Created new game! Transaction: " + result); setNewGameBet("0.01"); + setNewGameNickname(""); await new Promise((resolve) => setTimeout(resolve, 2000)); fetchActiveGames(); } catch (err: any) { @@ -165,6 +193,14 @@ export default function GameList({ ➕ Create New Game
+ setNewGameNickname(e.target.value)} + maxLength={20} + className="flex-1 min-w-[200px]" + />

- Enter the bet amount in ETH (e.g., 0.01 for 0.01 ETH). + Enter your nickname and bet amount in ETH (e.g., 0.01 for 0.01 ETH).

@@ -234,7 +270,10 @@ export default function GameList({

Player A

-

+

+ {game.playerA.nickname || "Unknown"} +

+

{formatAddress(game.playerA.addr)}

@@ -251,16 +290,25 @@ export default function GameList({

Player B

-

- {game.playerB.addr === "0x0000000000000000000000000000000000000000" - ? "⏳ Waiting..." - : formatAddress(game.playerB.addr)} -

+ {game.playerB.addr === "0x0000000000000000000000000000000000000000" ? ( +

+ ⏳ Waiting... +

+ ) : ( + <> +

+ {game.playerB.nickname || "Unknown"} +

+

+ {formatAddress(game.playerB.addr)} +

+ + )} {/* Join/Play Button */} -
+
{userGameIds.has(game.returnGameId) ? ( + ) : game.playerB.addr === "0x0000000000000000000000000000000000000000" ? ( +
+ { + const updatedNicknames = new Map(joinNicknames); + updatedNicknames.set(game.returnGameId, e.target.value); + setJoinNicknames(updatedNicknames); + }} + maxLength={20} + className="flex-1" + /> + +
) : ( )}
diff --git a/crypto_clash_frontend/app/clash/Reveal.tsx b/crypto_clash_frontend/app/clash/Reveal.tsx index f92b3b2..425b9e0 100644 --- a/crypto_clash_frontend/app/clash/Reveal.tsx +++ b/crypto_clash_frontend/app/clash/Reveal.tsx @@ -193,6 +193,26 @@ export default function Reveal({ return (
+ {/* Player Info */} + {gameDetails && ( +
+
+
+

You

+

+ {whoAmI === "player1" ? gameDetails.playerA.nickname : gameDetails.playerB.nickname} +

+
+
VS
+
+

Opponent

+

+ {whoAmI === "player1" ? gameDetails.playerB.nickname : gameDetails.playerA.nickname} +

+
+
+
+ )} {/* Show timeout result after game is inactive */} {isGameFinishedByTimeout && (