fix: so many bugs, feat: cancel tournaments

This commit is contained in:
2025-12-08 22:48:58 -05:00
Unverified
parent 5d821f732b
commit ad713965e5
7 changed files with 274 additions and 140 deletions

View File

@@ -10,32 +10,17 @@ public partial class AdminControls : HBoxContainer
public override void _Ready()
{
Connection.Instance.OnBecomeAdmin += OnBecomeAdmin;
Connection.Instance.OnTournamentEnd += _ => StartTournament.Show();
Connection.Instance.OnStartTournamentAck += () => StartTournament.Hide();
Connection.Instance.OnBecomeAdmin += UpdateUI;
Connection.Instance.OnTournamentEnd += UpdateUI;
Connection.Instance.OnStartTournamentAck += UpdateUI;
Connection.Instance.OnCancelTournamentAck += UpdateUI;
Connection.Instance.OnGetDataAcks += UpdateUI;
StartTournament.Pressed += () => Connection.Instance.StartTournament();
CancelTournament.Pressed += () => Connection.Instance.CancelTournament();
if (!Connection.Instance.IsAdmin)
{
StartTournament.Hide();
CancelTournament.Hide();
Label.Hide();
Timeout.Hide();
}
else if (Connection.Instance.ActiveTournament)
{
StartTournament.Hide();
}
Timeout.Value = Connection.Instance.CurrentWaitTimeout;
var time = Connection.Instance.CurrentWaitTimeout.ToString();
if (time.Length > 3)
{
time = time.Substring(0, 3);
}
Label.Text = "Wait To Move: " + time + "s ";
UpdateUI();
Timeout.ValueChanged += value =>
{
Connection.Instance.SetTournamentWait((float)value);
@@ -52,7 +37,54 @@ public partial class AdminControls : HBoxContainer
public override void _ExitTree()
{
Connection.Instance.OnBecomeAdmin -= OnBecomeAdmin;
Connection.Instance.OnBecomeAdmin -= UpdateUI;
Connection.Instance.OnTournamentEnd -= UpdateUI;
Connection.Instance.OnStartTournamentAck -= UpdateUI;
Connection.Instance.OnCancelTournamentAck -= UpdateUI;
Connection.Instance.OnGetDataAcks -= UpdateUI;
}
private void UpdateUI()
{
if (!Connection.Instance.IsAdmin)
{
BecomeAdmin.Show();
StartTournament.Hide();
CancelTournament.Hide();
Label.Hide();
Timeout.Hide();
}
else
{
BecomeAdmin.Hide();
Label.Show();
Timeout.Show();
}
if (Connection.Instance.IsAdmin && Connection.Instance.DemoMode)
{
StartTournament.Hide();
CancelTournament.Hide();
}
else if (Connection.Instance.IsAdmin && Connection.Instance.ActiveTournament)
{
StartTournament.Hide();
CancelTournament.Show();
}
else if (Connection.Instance.IsAdmin)
{
StartTournament.Show();
CancelTournament.Hide();
}
Timeout.Value = Connection.Instance.CurrentWaitTimeout;
var time = Connection.Instance.CurrentWaitTimeout.ToString();
if (time.Length > 3)
{
time = time.Substring(0, 3);
}
Label.Text = "Wait To Move: " + time + "s ";
}
private void ShowAuthPopup()
@@ -66,7 +98,7 @@ public partial class AdminControls : HBoxContainer
authWindow.Size = new Vector2I(256, 128);
authWindow.CloseRequested += () =>
{
GetTree().Root.RemoveChild(authWindow);
GetTree().Root.CallDeferred(Node.MethodName.RemoveChild, authWindow);
};
var vbox = new VBoxContainer();
@@ -81,12 +113,30 @@ public partial class AdminControls : HBoxContainer
passwordBox.PlaceholderText = "Password";
passwordBox.SetCustomMinimumSize(new Vector2(32, 32));
passwordBox.GuiInput += e =>
{
if (passwordBox.HasFocus() && e is InputEventKey inputEventKey && inputEventKey.IsPressed())
{
if (inputEventKey.KeyLabel == Key.Enter)
{
Connection.Instance.AdminAuth(passwordBox.Text);
GetTree().Root.CallDeferred(Node.MethodName.RemoveChild, authWindow);
GetViewport().SetInputAsHandled();
}
if (inputEventKey.KeyLabel == Key.Space)
{
GetViewport().SetInputAsHandled();
}
}
};
var button = new Button();
button.Text = "Login";
button.Pressed += () =>
{
Connection.Instance.AdminAuth(passwordBox.Text);
GetTree().Root.RemoveChild(authWindow);
GetTree().Root.CallDeferred(Node.MethodName.RemoveChild, authWindow);
};
vbox.AddChild(passwordBox);
@@ -96,19 +146,4 @@ public partial class AdminControls : HBoxContainer
GetTree().Root.AddChild(authWindow);
}
private void OnBecomeAdmin()
{
BecomeAdmin.Hide();
if (!Connection.Instance.ActiveTournament)
{
StartTournament.Show();
}
else
{
CancelTournament.Show();
}
Label.Show();
Timeout.Show();
}
}

View File

@@ -1,5 +1,4 @@
using Godot;
using System.Collections.Generic;
using System.Linq;
public partial class BoardScreen : Node2D
@@ -65,11 +64,10 @@ public partial class BoardScreen : Node2D
player2Card.GetNode<Label>("Status").Hide();
}
Connection.Instance.OnObserveWin += winner => { _lastMove = true; _winner = winner; };
Connection.Instance.OnObserveDraw += () => _lastMove = true;
Connection.Instance.OnObserveTerminated += () => PopupMessage("Match Terminated");
Connection.Instance.OnObserveWin += OnObserveWin;
Connection.Instance.OnObserveDraw += OnObserveDraw;
Connection.Instance.OnObserveTerminated += OnObserveTerminated;
Connection.Instance.OnObserveMove += ObserveMove;
Connection.Instance.OnTournamentEnd += ShowTournamentScoreboard;
}
public override void _Process(double delta)
@@ -114,20 +112,46 @@ public partial class BoardScreen : Node2D
public override void _ExitTree()
{
Connection.Instance.OnObserveWin -= OnObserveWin;
Connection.Instance.OnObserveDraw -= OnObserveDraw;
Connection.Instance.OnObserveTerminated -= OnObserveTerminated;
Connection.Instance.OnObserveMove -= ObserveMove;
Connection.Instance.OnTournamentEnd -= ShowTournamentScoreboard;
}
private void OnObserveWin(string winner)
{
_lastMove = true;
_winner = winner;
player1Card.GetNode<Label>("Status").Hide();
player2Card.GetNode<Label>("Status").Hide();
}
private void OnObserveDraw()
{
_lastMove = true;
player1Card.GetNode<Label>("Status").Hide();
player2Card.GetNode<Label>("Status").Hide();
}
private void OnObserveTerminated()
{
PopupMessage("Match Terminated");
player1Card.GetNode<Label>("Status").Hide();
player2Card.GetNode<Label>("Status").Hide();
}
private void ObserveMove(string username, int column)
{
if (username == matchData.player1)
{
if (!_lastMove)
player2Card.GetNode<Label>("Status").Show();
player1Card.GetNode<Label>("Status").Hide();
player2Card.GetNode<Label>("Status").Show();
}
else
{
player1Card.GetNode<Label>("Status").Show();
if (!_lastMove)
player1Card.GetNode<Label>("Status").Show();
player2Card.GetNode<Label>("Status").Hide();
}
Connection.Instance.PreviousMoves.Add((username, column));
@@ -160,39 +184,6 @@ public partial class BoardScreen : Node2D
TransitionToBracket();
}
private void ShowTournamentScoreboard(List<(string, int)> playerScoreboard)
{
var scoreboardWindow = new Window();
scoreboardWindow.AlwaysOnTop = true;
scoreboardWindow.MaximizeDisabled = true;
scoreboardWindow.Unresizable = true;
scoreboardWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen;
scoreboardWindow.Size = new Vector2I(256, 512);
scoreboardWindow.CloseRequested += () =>
{
GetTree().Root.RemoveChild(scoreboardWindow);
};
var tree = new Tree();
tree.HideRoot = true;
tree.Columns = 2;
tree.ColumnTitlesVisible = true;
tree.SetColumnTitle(0, "Player");
tree.SetColumnTitle(1, "Score");
var root = tree.CreateItem();
foreach ((string, int) entry in playerScoreboard)
{
var item = tree.CreateItem(root);
item.SetText(0, entry.Item1);
item.SetText(1, entry.Item2.ToString());
}
scoreboardWindow.AddChild(tree);
GetTree().Root.AddChild(scoreboardWindow);
}
private void TransitionToBracket()
{
GetTree().ChangeSceneToFile(BRACKET_SCENE_PATH);

View File

@@ -31,7 +31,6 @@ public partial class BracketScene : Control
Connection.Instance.OnUpdatedPlayers += UpdatePlayers;
Connection.Instance.OnUpdatedMatches += UpdateMatches;
Connection.Instance.OnWatchGameAck += TransitionToBoard;
Connection.Instance.OnTournamentEnd += ShowTournamentScoreboard;
}
public override void _ExitTree()
@@ -39,13 +38,13 @@ public partial class BracketScene : Control
Connection.Instance.OnUpdatedPlayers -= UpdatePlayers;
Connection.Instance.OnUpdatedMatches -= UpdateMatches;
Connection.Instance.OnWatchGameAck -= TransitionToBoard;
Connection.Instance.OnTournamentEnd -= ShowTournamentScoreboard;
}
private void UpdatePlayers(List<PlayerData> playerList)
{
Players.Clear();
_playerList = playerList;
_playerList.Sort((a, b) => a.username.CompareTo(b.username));
var root = Players.CreateItem();
for (int i = 0; i < _playerList.Count; i++)
{
@@ -55,7 +54,7 @@ public partial class BracketScene : Control
item.SetText(2, playerList[i].isPlaying ? "Yes" : "No");
if (Connection.Instance.IsAdmin)
{
item.AddButton(0, TerminateKickButton, i, false, "Kick"); // TODO
item.AddButton(0, TerminateKickButton, i, false, "Kick");
}
}
}
@@ -71,22 +70,30 @@ public partial class BracketScene : Control
item.SetText(0, matchList[i].matchId.ToString());
item.SetText(1, matchList[i].player1);
item.SetText(2, matchList[i].player2);
item.AddButton(0, WatchButton, item.GetButtonCount(0), false, "Watch");
item.AddButton(0, WatchButton, i, false, "Watch");
if (Connection.Instance.IsAdmin)
{
item.AddButton(0, TerminateKickButton, item.GetButtonCount(0), false, "Terminate");
item.AddButton(0, TerminateKickButton, 128 + i, false, "Terminate");
}
}
}
private void WatchGame(TreeItem item, long column, long id, long mouseButtonIndex)
{
if (mouseButtonIndex == 1 && column == 0)
if (mouseButtonIndex == 1 && column == 0 && id < 128)
{
Connection.Instance.SendWatchGame(_matchList[(int) id].matchId);
}
}
private void TerminateGame(TreeItem item, long column, long id, long mouseButtonIndex)
{
if (mouseButtonIndex == 1 && column == 0 && id - 128 >= 0 && _matchList[(int) id - 128] != null)
{
Connection.Instance.TerminateGame(_matchList[(int) id - 128].matchId);
}
}
private void KickPlayer(TreeItem item, long column, long id, long mouseButtonIndex)
{
if (mouseButtonIndex == 1 && column == 0)
@@ -95,51 +102,8 @@ public partial class BracketScene : Control
}
}
private void TerminateGame(TreeItem item, long column, long id, long mouseButtonIndex)
{
if (mouseButtonIndex == 1 && column == 0 && id - 1 > 0 && _matchList[(int) id - 1] != null)
{
Connection.Instance.TerminateGame(_matchList[(int) id - 1].matchId);
}
}
private void TransitionToBoard()
{
GetTree().ChangeSceneToFile(BOARD_SCENE_PATH);
}
private void ShowTournamentScoreboard(List<(string, int)> playerScoreboard)
{
var scoreboardWindow = new Window();
scoreboardWindow.Theme = GD.Load<Theme>("res://assets/theme.tres");
scoreboardWindow.AlwaysOnTop = true;
scoreboardWindow.MaximizeDisabled = true;
scoreboardWindow.Unresizable = true;
scoreboardWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen;
scoreboardWindow.Size = new Vector2I(256, 512);
scoreboardWindow.CloseRequested += () =>
{
GetTree().Root.RemoveChild(scoreboardWindow);
};
var tree = new Tree();
tree.HideRoot = true;
tree.Columns = 2;
tree.ColumnTitlesVisible = true;
tree.Theme = GD.Load<Theme>("res://assets/theme.tres");
tree.SetColumnTitle(0, "Player");
tree.SetColumnTitle(1, "Score");
var root = tree.CreateItem();
foreach ((string, int) entry in playerScoreboard)
{
var item = tree.CreateItem(root);
item.SetText(0, entry.Item1);
item.SetText(1, entry.Item2.ToString());
}
scoreboardWindow.AddChild(tree);
GetTree().Root.AddChild(scoreboardWindow);
}
}

View File

@@ -8,12 +8,55 @@ public partial class ConnectButtonUI : Button
public override void _Ready()
{
Connection.Instance.OnWsConnectionSuccess += () => GetTree().ChangeSceneToFile(BRACKET_SCENE_PATH);
Connection.Instance.OnWsConnectionFailed += () => ErrorLabel.Text = "Couldn't connect to server! " + Connection.Instance.LastError;
Connection.Instance.OnWsConnectionSuccess += OnConnectionSuccess;
Connection.Instance.OnWsConnectionFailed += OnConnectionFailed;
if (Connection.Instance.LastUsedConnectionAddress.Length > 0)
{
AddressField.Text = Connection.Instance.LastUsedConnectionAddress;
}
if (Connection.Instance.LastError.Length > 0)
{
ErrorLabel.Text = Connection.Instance.LastError;
}
AddressField.GuiInput += e =>
{
if (AddressField.HasFocus() && e is InputEventKey inputEventKey && inputEventKey.IsPressed())
{
if (inputEventKey.KeyLabel == Key.Enter)
{
Connection.Instance.Connect(AddressField.Text);
GetViewport().SetInputAsHandled();
}
if (inputEventKey.KeyLabel == Key.Space)
{
GetViewport().SetInputAsHandled();
}
}
};
}
public override void _ExitTree()
{
Connection.Instance.OnWsConnectionSuccess -= OnConnectionSuccess;
Connection.Instance.OnWsConnectionFailed -= OnConnectionFailed;
}
public override void _Pressed()
{
Connection.Instance.Connect(AddressField.Text);
}
private void OnConnectionSuccess()
{
GetTree().ChangeSceneToFile(BRACKET_SCENE_PATH);
}
private void OnConnectionFailed()
{
ErrorLabel.Text = "Couldn't connect to server! " + Connection.Instance.LastError;
}
}

View File

@@ -30,8 +30,10 @@ public partial class Connection : Node
public event Action<List<MatchData>> OnUpdatedMatches;
public event Action<List<PlayerData>> OnUpdatedPlayers;
public event Action OnStartTournamentAck;
public event Action<List<(string, int)>> OnTournamentEnd;
public event Action OnTournamentEnd;
public event Action OnCancelTournamentAck;
public event Action OnBecomeAdmin;
public event Action OnGetDataAcks;
public event Action OnWsConnectionSuccess;
public event Action OnWsConnectionFailed;
@@ -43,18 +45,19 @@ public partial class Connection : Node
public bool IsAdmin { get; private set; }
public bool IsPlayer { get; private set; }
public bool ActiveTournament { get; private set; }
public bool DemoMode { get; private set; }
public List<(string, int)> PreviousMoves { get; private set; } = [];
public double CurrentWaitTimeout { get; private set; } = 5.0;
public MatchData CurrentObservingMatch { get; private set; }
public string LastUsedConnectionAddress { get; private set; } = "";
public string LastError { get; private set; } = "";
private bool IsSocketOpen => _webSocket.GetReadyState() == WebSocketPeer.State.Open;
private bool _connecting = false;
private bool _connected = false;
public String LastError = "";
private List<(string, int)> _lastScoreboard = [];
private bool _shouldShowTournamentResults = false;
public override void _Ready()
{
@@ -67,6 +70,7 @@ public partial class Connection : Node
public void Connect(string address)
{
_connecting = true;
LastUsedConnectionAddress = address;
if (_connected)
{
return;
@@ -94,6 +98,7 @@ public partial class Connection : Node
{
_connecting = false;
_connected = true;
LastError = "";
OnWsConnectionSuccess?.Invoke();
StartGameListRefreshLoop();
}
@@ -103,6 +108,19 @@ public partial class Connection : Node
string message = _webSocket.GetPacket().GetStringFromUtf8();
HandleServerMessage(message);
}
if (_shouldShowTournamentResults)
{
var children = GetTree().Root.GetChildren();
foreach (var child in children)
{
if (child.Name.ToString() == "BracketView")
{
_shouldShowTournamentResults = false;
ShowTournamentScoreboard(_lastScoreboard);
}
}
}
}
else if (state == WebSocketPeer.State.Connecting)
{
@@ -122,9 +140,14 @@ public partial class Connection : Node
else if (_connected)
{
_connected = false;
IsAdmin = false;
CurrentWaitTimeout = 5.0;
ActiveTournament = false;
DemoMode = false;
StopGameListRefreshLoop();
var code = _webSocket.GetCloseCode();
var reason = _webSocket.GetCloseReason();
LastError = "Unexpected Disconnect. Reason: " + reason + ", Code: " + code;
GD.PrintErr("WebSocket closed with code: " + code + ", reason " + reason + ". Clean: " + (code != -1));
OnWsDisconnect?.Invoke();
}
@@ -222,6 +245,16 @@ public partial class Connection : Node
SendCommand("ADMIN", "AUTH:" + password);
}
public void GetMoveWait()
{
SendCommand("GET", "MOVE_WAIT");
}
public void GetTournamentStatus()
{
SendCommand("GET", "TOURNAMENT_STATUS");
}
// Admin commands
public void KickPlayer(string playerId)
@@ -330,7 +363,8 @@ public partial class Connection : Node
if (body == "AUTH:ACK")
{
IsAdmin = true;
SetTournamentWait(5.0f);
GetMoveWait();
GetTournamentStatus();
OnBecomeAdmin?.Invoke();
}
@@ -338,6 +372,26 @@ public partial class Connection : Node
case "TOURNAMENT":
HandleTournamentMessage(body);
break;
case "GET":
if (body.StartsWith("MOVE_WAIT"))
{
CurrentWaitTimeout = double.Parse(body.Split(":")[1]);
}
else if (body.StartsWith("TOURNAMENT_STATUS"))
{
string status = body.Split(":")[1];
if (status != "DEMO")
{
ActiveTournament = bool.Parse(status);
}
else
{
ActiveTournament = false;
DemoMode = true;
}
}
OnGetDataAcks?.Invoke();
break;
case "ERROR":
GD.PrintErr(message);
OnError?.Invoke(message);
@@ -372,7 +426,9 @@ public partial class Connection : Node
playerScoreboard.Add((data[0], int.Parse(data[1])));
}
OnTournamentEnd?.Invoke(playerScoreboard);
_lastScoreboard = playerScoreboard;
_shouldShowTournamentResults = true;
OnTournamentEnd?.Invoke();
break;
}
case "START":
@@ -381,6 +437,12 @@ public partial class Connection : Node
ActiveTournament = true;
break;
}
case "CANCEL":
{
ActiveTournament = false;
OnCancelTournamentAck?.Invoke();
break;
}
}
}
@@ -517,4 +579,42 @@ public partial class Connection : Node
}
}
}
public void ShowTournamentScoreboard(List<(string, int)> playerScoreboard)
{
var scoreboardWindow = new Window();
scoreboardWindow.Theme = GD.Load<Theme>("res://assets/theme.tres");
scoreboardWindow.AlwaysOnTop = true;
scoreboardWindow.MaximizeDisabled = true;
scoreboardWindow.Unresizable = true;
scoreboardWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen;
scoreboardWindow.Size = new Vector2I(256, 512);
scoreboardWindow.CloseRequested += () =>
{
GetTree().Root.RemoveChild(scoreboardWindow);
};
var tree = new Tree();
tree.HideRoot = true;
tree.Columns = 2;
tree.ColumnTitlesVisible = true;
tree.Theme = GD.Load<Theme>("res://assets/theme.tres");
tree.GrowHorizontal = Control.GrowDirection.Both;
tree.GrowVertical = Control.GrowDirection.Both;
tree.SetColumnTitle(0, "Player");
tree.SetColumnTitle(1, "Score");
var root = tree.CreateItem();
foreach ((string, int) entry in playerScoreboard)
{
var item = tree.CreateItem(root);
item.SetText(0, entry.Item1);
item.SetText(1, entry.Item2.ToString());
}
scoreboardWindow.AddChild(tree);
tree.SetAnchorsPreset(Control.LayoutPreset.FullRect);
GetTree().Root.AddChild(scoreboardWindow);
}
}

View File

@@ -10,5 +10,5 @@ func _ready() -> void:
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
func _process(_delta: float) -> void:
pass