add 新增远程连接,实现多人游戏
This commit is contained in:
101
Scripts/Controllers/ChessGame.cs
Normal file
101
Scripts/Controllers/ChessGame.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
public partial class ChessGame : Node2D
|
||||
{
|
||||
ChessBoard board;
|
||||
Global global;
|
||||
private bool isSession = false;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
// Init.Call();
|
||||
global = GetNode<Global>("/root/Global");
|
||||
board = GetNode<ChessBoard>("Chessboard");
|
||||
// GetNode<Button>("Undo").Connect("pressed", Callable.From(board.Undo));
|
||||
// GetNode<Button>("ReInit").Connect("pressed", Callable.From(board.ReInit));
|
||||
// GetNode<Button>("Home").Connect("pressed", Callable.From(this.GoHome));
|
||||
|
||||
if (!global.RPClient.GetIsConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSession = true;
|
||||
GD.Print("ws is connected");
|
||||
global.RPClient.OnPRCSessionExit += (cmd, code) => {
|
||||
GoHome();
|
||||
};
|
||||
board.Set("Hello", Callable.From(() => {GD.PrintErr("hello");}));
|
||||
board.Set("chessMoveFunc", Callable.From((Vector2 newPos, Vector2 fromPos) => {
|
||||
var res = global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
|
||||
{"type", "move"},
|
||||
{"from", fromPos},
|
||||
{"to", newPos},
|
||||
{"fromX", fromPos.X},
|
||||
{"fromY", fromPos.Y},
|
||||
{"toX", newPos.X},
|
||||
{"toY", newPos.Y},
|
||||
});
|
||||
GD.Print($"chessMoveFunc Callback {fromPos} -> {newPos} {res}");
|
||||
return false;
|
||||
}));
|
||||
global.RPClient.OnPRCSessionRecv += (msg) => {
|
||||
SessionMsgHandle(msg["msg"].AsGodotDictionary());
|
||||
};
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
public override void _Process(double delta) {
|
||||
}
|
||||
|
||||
private void SessionMsgHandle(Dictionary msg) {
|
||||
// GD.PrintErr($"session msg: {msg}");
|
||||
switch (msg["type"].AsString()) {
|
||||
case "move":
|
||||
Vector2 to = new(GD.StrToVar(msg["toX"].ToString()).AsInt32(),
|
||||
GD.StrToVar(msg["toY"].ToString()).AsInt32());
|
||||
|
||||
Vector2 from = new(GD.StrToVar(msg["fromX"].ToString()).AsInt32(),
|
||||
GD.StrToVar(msg["fromY"].ToString()).AsInt32());
|
||||
board.MoveAndRecord(to, from);
|
||||
break;
|
||||
case "undo":
|
||||
board.Undo();
|
||||
break;
|
||||
case "reInit":
|
||||
board.ReInit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void GoHome() {
|
||||
if (global.RPClient.IsOnline()) {
|
||||
global.RPClient.ExitServer();
|
||||
}
|
||||
global.GotoScene("res://Main.tscn");
|
||||
}
|
||||
|
||||
public void Undo() {
|
||||
GD.PrintErr($"Undo {isSession}");
|
||||
if (isSession == false) {
|
||||
GD.PrintErr($"Undo ??");
|
||||
board.Undo();
|
||||
return;
|
||||
}
|
||||
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
|
||||
{"type", "undo"},
|
||||
});
|
||||
}
|
||||
|
||||
public void ReInit() {
|
||||
GD.PrintErr($"ReInit {isSession}");
|
||||
if (isSession == false) {
|
||||
board.ReInit();
|
||||
return;
|
||||
}
|
||||
global.RPClient.SendSessionToAll(global.sessionId, new Dictionary{
|
||||
{"type", "reInit"},
|
||||
});
|
||||
}
|
||||
}
|
136
Scripts/Controllers/Menu.cs
Normal file
136
Scripts/Controllers/Menu.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
public partial class Menu : Control
|
||||
{
|
||||
Global global = null;
|
||||
ItemList lists = null;
|
||||
ConfirmationDialog dialog = null;
|
||||
LineEdit nameLineEdit = null;
|
||||
LineEdit urlLineEdit = null;
|
||||
ColorRect colorRect = null;
|
||||
Timer timer = null;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
global = GetNode<Global>("/root/Global");
|
||||
nameLineEdit = GetNode<LineEdit>("Name/LineEdit");
|
||||
urlLineEdit = GetNode<LineEdit>("URL");
|
||||
urlLineEdit.Text = global.URL;
|
||||
urlLineEdit.Set("text_submitted", Callable.From(()=>{
|
||||
global.URL = urlLineEdit.Text;
|
||||
}));
|
||||
|
||||
lists = GetNode<ItemList>("Server/ItemList");
|
||||
dialog = GetNode<ConfirmationDialog>("Dialogs/ConfirmationDialog");
|
||||
colorRect = GetNode<ColorRect>("Server/ColorRect");
|
||||
dialog.DialogAutowrap = true;
|
||||
dialog.MinSize = new Vector2I(400, 200);
|
||||
dialog.Canceled += () => {
|
||||
if (dialog.Title == "Session Created")
|
||||
global.RPClient.SessionAckCreate(dialog.GetMeta("sessionId").ToString(), false);
|
||||
};
|
||||
dialog.Confirmed += () => {
|
||||
// GD.PrintErr("confirm", dialog.GetLabel().Text);
|
||||
// goToSignle();
|
||||
if (dialog.Title == "Session Created")
|
||||
global.RPClient.SessionAckCreate(dialog.GetMeta("sessionId").ToString(), true);
|
||||
};
|
||||
|
||||
global.RPClient.RegSessionAckCreateCallback((
|
||||
sessionId,
|
||||
res,
|
||||
reqUserId,
|
||||
reqUserName) => {
|
||||
if (reqUserId != null) {
|
||||
dialog.Title = "Session Created";
|
||||
dialog.SetMeta("reqUserName", reqUserName);
|
||||
dialog.SetMeta("reqUserId", reqUserId);
|
||||
dialog.SetMeta("sessionId", sessionId);
|
||||
// dialog.GetLabel==>Text = $"{sessdata["reqUserName"]}";
|
||||
dialog.DialogText = $"username: {reqUserName}\n" +
|
||||
$"reqUserId: {reqUserId}\n";
|
||||
dialog.Visible = true;
|
||||
} else {
|
||||
if (res) {
|
||||
global.sessionId = sessionId;
|
||||
global.GotoScene("res://Scenes/ChessGame.tscn");
|
||||
} else {
|
||||
dialog.Title = "Failed";
|
||||
dialog.DialogText = $"session create failed";
|
||||
dialog.Visible = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
timer = new Timer();
|
||||
AddChild(timer);
|
||||
timer.Connect("timeout", Callable.From(() => {
|
||||
if (global.RPClient.GetIsConnected()) {
|
||||
colorRect.Color = Colors.Green;
|
||||
} else {
|
||||
colorRect.Color = Colors.Red;
|
||||
}
|
||||
}));
|
||||
timer.Start(1);
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
public override void _Process(double delta) {
|
||||
}
|
||||
|
||||
private void OnItemSelected(int index) {
|
||||
Dictionary item = lists.GetItemMetadata(index).AsGodotDictionary();
|
||||
GD.Print($"Item {index} selected, {item}");
|
||||
string[] strings = { item["id"].ToString() };
|
||||
global.RPClient.SessionCreate(strings);
|
||||
}
|
||||
|
||||
private void FlushData()
|
||||
{
|
||||
global.RPClient.RegionInspect("server", (data) => {
|
||||
GD.Print(data);
|
||||
lists.Clear();
|
||||
foreach (Dictionary<string, string> user in data) {
|
||||
string userId = user["id"].ToString();
|
||||
string userName = user["name"].ToString();
|
||||
if (userId == global.RPClient.GetUserId()) {
|
||||
lists.SetItemDisabled(
|
||||
lists.AddItem($"Name: {userName}"),
|
||||
true);
|
||||
continue;
|
||||
}
|
||||
var idx = lists.AddItem($"Name: {userName}");
|
||||
lists.SetItemMetadata(idx, user);
|
||||
lists.SetItemTooltip(idx, $"User ID: {userId}");
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
GD.Print("Connect");
|
||||
global.RPClient.ExitServer();
|
||||
global.RPClient.ConnectToUrlEx(urlLineEdit.Text);
|
||||
global.SetProcess(true);
|
||||
}
|
||||
|
||||
public void EnterName() {
|
||||
var newLine = nameLineEdit.Text;
|
||||
global.RPClient.UserRename(newLine);
|
||||
nameLineEdit.Text = newLine;
|
||||
}
|
||||
|
||||
private void goToHome() {
|
||||
global.RPClient.ExitServer();
|
||||
global.GotoScene("res://Main.tscn");
|
||||
}
|
||||
|
||||
// private void OnItemListItemClicked(int index, Vector2 atPosition, int mouse_button_index)
|
||||
// {
|
||||
// GD.Print($"Item {index} clicked at {atPosition} with mouse button index {mouse_button_index}");
|
||||
// }
|
||||
}
|
@ -6,6 +6,10 @@ public partial class ChessBoard : Node2D {
|
||||
MoveRecords Records = null;
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public Vector2 selectedNodePos = Vector2.Inf;
|
||||
|
||||
public delegate bool ChessMoveFunc(Vector2 toPos, Vector2 fromPos);
|
||||
public Callable chessMoveFunc { get; set; }
|
||||
|
||||
public override void _Ready() {
|
||||
Board = new VirtualBoard(this as Node);
|
||||
Records = new MoveRecords(onUndoRecordCallback: (newNode, oldNode, newPos, oldPos) => {
|
||||
@ -16,72 +20,55 @@ public partial class ChessBoard : Node2D {
|
||||
if (newPiece != null) {
|
||||
Board.InsertNode(newPiece, newPos);
|
||||
}
|
||||
// if (oldPiece != null) {
|
||||
// Board.RemoveNode(oldPos);
|
||||
// }
|
||||
});
|
||||
Board.InitChessBoard();
|
||||
// this.AddChild();
|
||||
}
|
||||
|
||||
|
||||
public override void _Input(InputEvent @event) {
|
||||
if (@event is InputEventMouseButton mouseEvent &&
|
||||
mouseEvent.Pressed &&
|
||||
mouseEvent.ButtonIndex == MouseButton.Left) {
|
||||
// HandleMouseClick(GetGlobalTransformWithCanvas().AffineInverse() * mouseButton.Position);
|
||||
// HandleMouseClick(GetLocalMousePosition());
|
||||
ActionPos(
|
||||
(PosTrans.transArrToPix.AffineInverse() *
|
||||
GetLocalMousePosition()).Round()
|
||||
);
|
||||
HandleMouseClick(GetLocalMousePosition());
|
||||
// ActionPos(
|
||||
// (PosTrans.transArrToPix.AffineInverse() *
|
||||
// GetLocalMousePosition()).Round()
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
public void ActionPos(Vector2 clickArrPos) {
|
||||
if (VirtualBoard.ArrPosOutOfRange(clickArrPos)) return;
|
||||
GD.Print($"{clickArrPos} mouse clicked");
|
||||
ChessPiece NowChess = Board.getNodeFromBoard(clickArrPos) as ChessPiece;
|
||||
ChessPiece SelChess = Board.getNodeFromBoard(selectedNodePos) as ChessPiece;
|
||||
|
||||
if (Vector2.Inf.Equals(selectedNodePos)) {
|
||||
if (NowChess == null) {
|
||||
return;
|
||||
}
|
||||
NowChess.Selected();
|
||||
GD.Print($"{clickArrPos} is selected");
|
||||
selectedNodePos = clickArrPos;
|
||||
} else if (selectedNodePos == clickArrPos) {
|
||||
if (SelChess != null) {
|
||||
SelChess.DeSelected();
|
||||
}
|
||||
GD.Print($"{selectedNodePos} is deselected");
|
||||
selectedNodePos = Vector2.Inf;
|
||||
} else {
|
||||
GD.Print($"{selectedNodePos} move to {clickArrPos}");
|
||||
if (SelChess != null) {
|
||||
SelChess.DeSelected();
|
||||
GD.Print($"{selectedNodePos} is deselected");
|
||||
}
|
||||
Node NowNode;
|
||||
if (NowChess != null) {
|
||||
GD.Print("nowchess", NowChess);
|
||||
NowNode = NowChess.Duplicate();
|
||||
Board.RemoveNode(clickArrPos);
|
||||
} else {
|
||||
NowNode = NowChess as Node;
|
||||
}
|
||||
Records.AddRecord(NowNode, SelChess, clickArrPos, selectedNodePos);
|
||||
Board.MoveNode(clickArrPos, selectedNodePos);
|
||||
selectedNodePos = Vector2.Inf;
|
||||
// GD.Print($"End: {selectedNodePos} {SelChess} move to {clickArrPos} {NowChess}");
|
||||
}
|
||||
public void ActionPos(Vector2 newPos) {
|
||||
}
|
||||
|
||||
public void undo() {
|
||||
public void MoveAndRecord(Vector2 toPos, Vector2 fromPos) {
|
||||
GD.Print($"{fromPos} move to {toPos}");
|
||||
ChessPiece toChess = Board.GetNodeFromBoard(toPos) as ChessPiece;
|
||||
ChessPiece fromChess = Board.GetNodeFromBoard(fromPos) as ChessPiece;
|
||||
|
||||
if (fromChess != null) {
|
||||
fromChess.DeSelected();
|
||||
}
|
||||
|
||||
Node NowNode;
|
||||
if (toChess != null) {
|
||||
GD.Print("nowchess", toChess);
|
||||
NowNode = toChess.Duplicate();
|
||||
Board.RemoveNode(toPos);
|
||||
} else {
|
||||
NowNode = toChess as Node;
|
||||
}
|
||||
Records.AddRecord(NowNode, fromChess, toPos, fromPos);
|
||||
Board.MoveNode(toPos, fromPos);
|
||||
|
||||
selectedNodePos = Vector2.Inf;
|
||||
}
|
||||
|
||||
public void Undo() {
|
||||
Records.Undo();
|
||||
}
|
||||
|
||||
public void redo() {
|
||||
public void ReInit() {
|
||||
Records.Clear();
|
||||
Board.Clear();
|
||||
selectedNodePos = Vector2.Inf;
|
||||
@ -89,13 +76,39 @@ public partial class ChessBoard : Node2D {
|
||||
}
|
||||
|
||||
private void HandleMouseClick(Vector2 clickPosition) {
|
||||
Vector2 newPos = (PosTrans.transArrToPix.AffineInverse() *
|
||||
clickPosition).Round();
|
||||
|
||||
// 如果有棋子被选中,则进行后续操作
|
||||
// if (nowSelectedChess == null) {
|
||||
// seletedNode = null;
|
||||
// } else if (nowSelectedChess != null && seletedNode != null) {
|
||||
// if (seletedNode is ChessPiece chessPiece) MoveChess(arrayPos, chessPiece.arrPos);
|
||||
// }
|
||||
if (VirtualBoard.ArrPosOutOfRange(newPos)) return;
|
||||
GD.Print($"{newPos} mouse clicked");
|
||||
ChessPiece NowChess = Board.GetNodeFromBoard(newPos) as ChessPiece;
|
||||
ChessPiece SelChess = Board.GetNodeFromBoard(selectedNodePos) as ChessPiece;
|
||||
|
||||
if (Vector2.Inf.Equals(selectedNodePos)) {
|
||||
if (NowChess == null) {
|
||||
return;
|
||||
}
|
||||
NowChess.Selected();
|
||||
selectedNodePos = newPos;
|
||||
} else if (selectedNodePos == newPos) {
|
||||
if (SelChess != null) {
|
||||
SelChess.DeSelected();
|
||||
}
|
||||
selectedNodePos = Vector2.Inf;
|
||||
} else {
|
||||
if (chessMoveFunc.Delegate != null) {
|
||||
GD.Print("chessMoveFunc Move: ", selectedNodePos, "->", newPos);
|
||||
if ((bool)chessMoveFunc.Call(newPos, selectedNodePos)) {
|
||||
MoveAndRecord(newPos, selectedNodePos);
|
||||
}
|
||||
} else {
|
||||
GD.Print("default MoveFunc Move: ", selectedNodePos, "->", newPos);
|
||||
MoveAndRecord(newPos, selectedNodePos);
|
||||
}
|
||||
|
||||
// MoveAndRecord(newPos, selectedNodePos);
|
||||
// GD.Print($"End: {selectedNodePos} {SelChess} move to {clickArrPos} {NowChess}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,6 +35,7 @@ public partial class ChessPiece : Sprite2D {
|
||||
if (isSelected) {
|
||||
return;
|
||||
}
|
||||
GD.Print($"{arrPos} is selected");
|
||||
this.Transform *= transToSeleted;
|
||||
this.isSelected = true;
|
||||
}
|
||||
@ -43,6 +44,7 @@ public partial class ChessPiece : Sprite2D {
|
||||
if (!isSelected) {
|
||||
return;
|
||||
}
|
||||
GD.Print($"{arrPos} is deselected");
|
||||
this.Transform *= transToSeleted.AffineInverse();
|
||||
this.isSelected = false;
|
||||
}
|
||||
@ -54,7 +56,7 @@ public partial class ChessPiece : Sprite2D {
|
||||
|
||||
private void InitLabel() {
|
||||
// this.Texture.ResourcePath = "res://Asserts/ChesspieceBase.tres";
|
||||
this.Texture = (Texture2D)ResourceLoader.Load("res://Asserts/ChesspieceBase.tres");
|
||||
this.Texture ??= (Texture2D)ResourceLoader.Load("res://Asserts/ChesspieceBase.tres");
|
||||
textureSize = this.Texture.GetSize();
|
||||
Vector2 labalPosition = new(
|
||||
-textureSize.X / 2,
|
||||
|
85
Scripts/Global.cs
Normal file
85
Scripts/Global.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using Godot;
|
||||
using RPPackage;
|
||||
|
||||
public partial class Global : Node
|
||||
{
|
||||
public RPClientEDWS RPClient = new();
|
||||
public string sessionId;
|
||||
public string URL = "wss://game.zzyxyz.com/";
|
||||
public Node CurrentScene { get; set; }
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
RPClient.OnRPCError += (string errCode, string type, string cmd, string errMsg) => {
|
||||
GD.PrintErr($"errCode {errCode}, type/cmd {type}/{cmd}, errMsg {errMsg}");
|
||||
};
|
||||
RPClient.OnClose += (string eventName, object[] args) => {
|
||||
SetProcess(false);
|
||||
};
|
||||
RPClient.OnOpen += (string eventName, object[] args) => {
|
||||
RPClient.UserInit("undefined", "godot chessboard", () => {
|
||||
return RPClient.RegionAdd("server");
|
||||
});
|
||||
};
|
||||
Viewport root = GetTree().Root;
|
||||
CurrentScene = root.GetChild(root.GetChildCount() - 1);
|
||||
SetProcess(false);
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
RPClient.PollEx(delta);
|
||||
}
|
||||
|
||||
public override void _Notification(int what)
|
||||
{
|
||||
if (what == NotificationWMCloseRequest) {
|
||||
RPClient.Close();
|
||||
GetTree().Quit(); // default behavior
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void ChangeSceneCallback(Node newSence);
|
||||
private static ChangeSceneCallback changeSceneCallback = null;
|
||||
public void GotoScene(string path, ChangeSceneCallback callback = null)
|
||||
{
|
||||
// This function will usually be called from a signal callback,
|
||||
// or some other function from the current scene.
|
||||
// Deleting the current scene at this point is
|
||||
// a bad idea, because it may still be executing code.
|
||||
// This will result in a crash or unexpected behavior.
|
||||
|
||||
// The solution is to defer the load to a later time, when
|
||||
// we can be sure that no code from the current scene is running:
|
||||
if (callback != null) {
|
||||
changeSceneCallback = callback;
|
||||
}
|
||||
Callable callbackWrapper = new(null, nameof(changeSceneCallback));
|
||||
|
||||
CallDeferred(MethodName.DeferredGotoScene, path, callbackWrapper);
|
||||
changeSceneCallback = null;
|
||||
}
|
||||
|
||||
public void DeferredGotoScene(string path, Callable onLoaded)
|
||||
{
|
||||
// It is now safe to remove the current scene.
|
||||
CurrentScene.Free();
|
||||
|
||||
// Load a new scene.
|
||||
var nextScene = GD.Load<PackedScene>(path);
|
||||
|
||||
// Instance the new scene.
|
||||
CurrentScene = nextScene.Instantiate();
|
||||
if (changeSceneCallback != null) {
|
||||
onLoaded.Call(CurrentScene);
|
||||
}
|
||||
|
||||
// Add it to the active scene, as child of root.
|
||||
GetTree().Root.AddChild(CurrentScene);
|
||||
|
||||
// Optionally, to make it compatible with the SceneTree.change_scene_to_file() API.
|
||||
GetTree().CurrentScene = CurrentScene;
|
||||
}
|
||||
}
|
320
Scripts/Lib/RPClient.cs
Normal file
320
Scripts/Lib/RPClient.cs
Normal file
@ -0,0 +1,320 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace RPPackage {
|
||||
public partial class RPClientBaseEDWS : EventDrivenWebSocket {
|
||||
|
||||
public delegate void RPClientEventHandler(string cmd, Dictionary data);
|
||||
public delegate void RPClientErrorHandler(string errCode, string type, string cmd, string errMsg);
|
||||
|
||||
public event RPClientEventHandler OnRPCUser;
|
||||
public event RPClientEventHandler OnRPCRegion;
|
||||
public event RPClientEventHandler OnRPCSession;
|
||||
public event RPClientEventHandler OnRPCMsg;
|
||||
public event RPClientErrorHandler OnRPCError;
|
||||
|
||||
public RPClientBaseEDWS() : base() {
|
||||
OnText += (text) => {
|
||||
GD.Print($"response: {text}");
|
||||
RPMessage msg = RPHelper.HandleIncomingMessage(text);
|
||||
if (msg.Code != "0000") {
|
||||
OnRPCError?.Invoke(msg.Code, msg.Type, msg.Cmd, msg.Data.ToString());
|
||||
return;
|
||||
}
|
||||
switch (msg.Type) {
|
||||
case "user":
|
||||
OnRPCUser?.Invoke(msg.Cmd, msg.Data);
|
||||
break;
|
||||
case "region":
|
||||
OnRPCRegion?.Invoke(msg.Cmd, msg.Data);
|
||||
break;
|
||||
case "session":
|
||||
OnRPCSession?.Invoke(msg.Cmd, msg.Data);
|
||||
break;
|
||||
case "msg":
|
||||
OnRPCMsg?.Invoke(msg.Cmd, msg.Data);
|
||||
break;
|
||||
default:
|
||||
OnRPCError?.Invoke(msg.Code, "unknown", msg.Cmd, msg.Data.ToString());
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected void MakeRPCError(string errCode, string type, string cmd, string errMsg) {
|
||||
this.OnRPCError?.Invoke(errCode, type, cmd, errMsg ?? "null");
|
||||
}
|
||||
}
|
||||
|
||||
public partial class RPClientEDWS : RPClientBaseEDWS {
|
||||
string userName;
|
||||
string userId;
|
||||
string userToken;
|
||||
string regionId;
|
||||
|
||||
public string GetUserId() { return userId; }
|
||||
|
||||
public delegate void SessionRecvHandle(Dictionary msg);
|
||||
public event SessionRecvHandle OnPRCSessionRecv;
|
||||
|
||||
public event RPClientEventHandler OnPRCSessionExit;
|
||||
|
||||
public RPClientEDWS() : base() {
|
||||
OnRPCUser += (cmd, msg) => {
|
||||
switch (cmd) {
|
||||
case "init":
|
||||
userId = msg["userId"].AsString();
|
||||
userToken = msg["token"].AsString();
|
||||
_userInitCallback?.Invoke();
|
||||
break;
|
||||
case "rename":
|
||||
userName = msg["_"].AsString();
|
||||
break;
|
||||
default:
|
||||
MakeRPCError("0000", "user", "unknown", msg?.ToString());
|
||||
break;
|
||||
}
|
||||
};
|
||||
OnRPCRegion += (cmd, msg) => {
|
||||
switch (cmd) {
|
||||
case "add":
|
||||
break;
|
||||
case "inspect":
|
||||
// var regions = msg["_"].AsGodotArray<Dictionary>();
|
||||
_regionRecvCallback?.Invoke(cmd, msg);
|
||||
break;
|
||||
case "list":
|
||||
// var users = msg["_"].AsGodotArray<Dictionary>();
|
||||
_regionRecvCallback?.Invoke(cmd, msg);
|
||||
break;
|
||||
default:
|
||||
MakeRPCError("0000", "region", "unknown", msg?.ToString());
|
||||
break;
|
||||
}
|
||||
};
|
||||
OnRPCSession += (cmd, msg) => {
|
||||
switch (cmd) {
|
||||
case "sendAll":
|
||||
OnPRCSessionRecv?.Invoke(msg);
|
||||
break;
|
||||
case "create":
|
||||
break;
|
||||
case "ackCreate":
|
||||
// GD.PrintErr($"{cmd} {msg} {__SessionAckCreateCallback__ == null}");
|
||||
_sessionAckCreateCallback?.Invoke(cmd, msg);
|
||||
break;
|
||||
case "exit":
|
||||
OnPRCSessionExit?.Invoke(cmd, msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
OnRPCMsg += (cmd, msg) => {
|
||||
switch(cmd) {
|
||||
case "echo":
|
||||
GD.Print(msg["_"].AsString());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
OnRPCError += (code, type, cmd, msg) => {
|
||||
};
|
||||
}
|
||||
|
||||
public void ClearRPCClientFunc() {
|
||||
|
||||
}
|
||||
|
||||
public bool IsOnline() {
|
||||
return GetIsConnected() && userId != null;
|
||||
}
|
||||
|
||||
public void ConnectServer(string url) {
|
||||
|
||||
}
|
||||
|
||||
public void ExitServer() {
|
||||
this.SendRPMessage(new RPMessage {
|
||||
Type = "user",
|
||||
Cmd = "exit",
|
||||
Uid = userId,
|
||||
Token = userToken,
|
||||
});
|
||||
userId = null;
|
||||
userToken = null;
|
||||
this.Close();
|
||||
}
|
||||
|
||||
public delegate bool UserInitCallback();
|
||||
private UserInitCallback _userInitCallback;
|
||||
public bool UserInit(string userName, string fingerPrint, UserInitCallback callback) {
|
||||
if (this.GetIsConnected() == false) {
|
||||
return false;
|
||||
}
|
||||
this.SendRPMessage(new RPMessage{
|
||||
Type = "user",
|
||||
Cmd = "init",
|
||||
Data = new Dictionary {
|
||||
{ "userName", userName },
|
||||
{ "fingerPrint", fingerPrint }
|
||||
}
|
||||
});
|
||||
_userInitCallback = null;
|
||||
if (callback != null) _userInitCallback += callback;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool UserRename(string newName) {
|
||||
if (this.GetIsConnected() == false) {
|
||||
return false;
|
||||
}
|
||||
this.SendRPMessage(new RPMessage{
|
||||
Type = "user",
|
||||
Cmd = "rename",
|
||||
Uid = userId,
|
||||
Token = userToken,
|
||||
Data = new Dictionary {
|
||||
{ "_", newName }
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RegionAdd(string regionId) {
|
||||
if (this.GetIsConnected() == false || this.userId == null) {
|
||||
return false;
|
||||
}
|
||||
this.SendRPMessage(new RPMessage{
|
||||
Type = "region",
|
||||
Cmd = "add",
|
||||
Uid = userId,
|
||||
Token = userToken,
|
||||
Data = new Dictionary {
|
||||
{ "regionId", regionId }
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public delegate bool RegionInspectCallback(
|
||||
Array<Dictionary<string, string>> _
|
||||
);
|
||||
private RPClientEventHandler _regionRecvCallback;
|
||||
public bool RegionInspect(string regionId, RegionInspectCallback callback) {
|
||||
if (this.GetIsConnected() == false || this.userId == null) {
|
||||
return false;
|
||||
}
|
||||
_regionRecvCallback = null;
|
||||
_regionRecvCallback += (cmd, msg) => {
|
||||
if (cmd != "inspect") {
|
||||
return;
|
||||
}
|
||||
callback(msg["_"].AsGodotArray<Dictionary<string, string>>());
|
||||
};
|
||||
this.SendRPMessage(new RPMessage{
|
||||
Type = "region",
|
||||
Cmd = "inspect",
|
||||
Uid = userId,
|
||||
Token = userToken,
|
||||
Data = new Dictionary {
|
||||
{ "regionId", regionId }
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public delegate bool SessionAckCreateCallback(
|
||||
string sessionId,
|
||||
bool res,
|
||||
string reqUserId,
|
||||
string reqUserName
|
||||
);
|
||||
private RPClientEventHandler _sessionAckCreateCallback;
|
||||
public void RegSessionAckCreateCallback(SessionAckCreateCallback recvCallback) {
|
||||
_sessionAckCreateCallback = null;
|
||||
_sessionAckCreateCallback += (cmd, msg) => {
|
||||
string sessionId = msg["sessionId"].AsString();
|
||||
bool res = msg.ContainsKey("res") && msg["res"].AsBool();
|
||||
string reqUserId = msg.ContainsKey("reqUserId") ? msg["reqUserId"].AsString() : null;
|
||||
string reqUserName = msg.ContainsKey("reqUserName") ? msg["reqUserName"].AsString() : null;
|
||||
recvCallback(sessionId, res, reqUserId, reqUserName);
|
||||
};
|
||||
}
|
||||
public bool SessionCreate(string[] usersId) {
|
||||
if (this.GetIsConnected() == false || this.userId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.SendRPMessage(new RPMessage {
|
||||
Type = "session",
|
||||
Cmd = "create",
|
||||
Uid = userId,
|
||||
Token = userToken,
|
||||
Data = new Dictionary {
|
||||
{ "_", usersId }
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SessionAckCreate(string sessionId, bool response)
|
||||
{
|
||||
if (this.GetIsConnected() == false || this.userId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据响应情况发送确认消息给服务器
|
||||
string code = response ? "0000" : "0001";
|
||||
this.SendRPMessage(new RPMessage {
|
||||
Type = "session",
|
||||
Cmd = "ackCreate",
|
||||
Uid = userId,
|
||||
Token = userToken,
|
||||
Code = code,
|
||||
Data = new Dictionary {
|
||||
{ "sessionId", sessionId },
|
||||
{ "res", response }
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SendSessionToAll(string sessionId, Variant data) {
|
||||
if (this.GetIsConnected() == false || this.userId == null || sessionId == null) {
|
||||
return false;
|
||||
}
|
||||
this.SendRPMessage(new RPMessage{
|
||||
Type = "session",
|
||||
Cmd = "sendAll",
|
||||
Uid = userId,
|
||||
Token = userToken,
|
||||
Data = new Dictionary {
|
||||
{ "sessionId", sessionId },
|
||||
{ "msg", data },
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// ws.OnOpen += (eventName, args) => {
|
||||
// ws.SendRPMessage(new RPMessage{
|
||||
// Type = "user",
|
||||
// Cmd = "tmp",
|
||||
// });
|
||||
// };
|
||||
|
||||
// ws.OnError += (eventName, args) => {
|
||||
// GD.PrintErr(args);
|
||||
// // SetProcess(false);
|
||||
// };
|
||||
|
||||
// ws.OnClose += (eventName, args) => {
|
||||
// // GD.Print("close");
|
||||
// SetProcess(false);
|
||||
// };
|
||||
}
|
||||
}
|
45
Scripts/Lib/RPHelper.cs
Normal file
45
Scripts/Lib/RPHelper.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace RPPackage {
|
||||
public static class RPHelper
|
||||
{
|
||||
public static Dictionary SerializeRPMessage(RPMessage message)
|
||||
{
|
||||
return message.ToDictionary();
|
||||
}
|
||||
|
||||
public static RPMessage DeserializeRPMessage(Dictionary data)
|
||||
{
|
||||
return new RPMessage
|
||||
{
|
||||
Type = data.ContainsKey("type") ? (string)data["type"] : null,
|
||||
Cmd = data.ContainsKey("cmd") ? (string)data["cmd"] : null,
|
||||
Code = data.ContainsKey("code") ? (string)data["code"] : null,
|
||||
Uid = data.ContainsKey("uid") ? (string)data["uid"] : null,
|
||||
Token = data.ContainsKey("token") ? (string)data["token"] : null,
|
||||
Data = data.ContainsKey("data") ? data["data"].AsGodotDictionary() : null,
|
||||
};
|
||||
}
|
||||
|
||||
// 假设你有WebSocket通信的实现,这里仅展示如何使用上述方法
|
||||
public static void SendRPMessage(this EventDrivenWebSocket ws, RPMessage message)
|
||||
{
|
||||
ws.SendJsonEx(RPHelper.SerializeRPMessage(message));
|
||||
}
|
||||
|
||||
public static RPMessage HandleIncomingMessage(string jsonMessage)
|
||||
{
|
||||
var dataDict = Json.ParseString(jsonMessage).AsGodotDictionary();
|
||||
if (dataDict != null)
|
||||
{
|
||||
return RPHelper.DeserializeRPMessage(dataDict);
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.PrintErr("Failed to parse incoming message.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
Scripts/Lib/RPMessage.cs
Normal file
33
Scripts/Lib/RPMessage.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace RPPackage {
|
||||
public class RPMessage
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public string Cmd { get; set; }
|
||||
public Dictionary Data { get; set; }
|
||||
|
||||
public string Uid { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string Code { get; set; }
|
||||
public Dictionary ToDictionary()
|
||||
{
|
||||
var dict = new Dictionary();
|
||||
if (Type != null)
|
||||
dict.Add("type", Type);
|
||||
if (Cmd != null)
|
||||
dict.Add("cmd", Cmd);
|
||||
if (Data != null)
|
||||
dict.Add("data", Data);
|
||||
|
||||
if (Uid != null)
|
||||
dict.Add("uid", Uid);
|
||||
if (Token != null)
|
||||
dict.Add("token", Token);
|
||||
if (Code != null)
|
||||
dict.Add("code", Code);
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
0
Scripts/Lib/csws.cs
Normal file
0
Scripts/Lib/csws.cs
Normal file
114
Scripts/Lib/gdws.cs
Normal file
114
Scripts/Lib/gdws.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
public partial class EventDrivenWebSocket : WebSocketPeer {
|
||||
public delegate void WebSocketEventHandler(string eventName, params object[] args);
|
||||
public delegate void WSBinMsgEventHandler(byte[] args);
|
||||
public delegate void WSMsgEventHandler(string args);
|
||||
|
||||
public event WebSocketEventHandler OnOpen;
|
||||
public event WSBinMsgEventHandler OnMessage;
|
||||
public event WSMsgEventHandler OnText;
|
||||
public event WSBinMsgEventHandler OnBinary;
|
||||
public event WebSocketEventHandler OnClose;
|
||||
public event WebSocketEventHandler OnError;
|
||||
|
||||
private bool isConnected = false;
|
||||
private bool isCloseEventFired = false;
|
||||
private double connectingTime = double.NegativeInfinity;
|
||||
|
||||
|
||||
public EventDrivenWebSocket() : base() {
|
||||
isConnected = false;
|
||||
}
|
||||
|
||||
public bool GetIsConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
public void PollEx(double delta) {
|
||||
base.Poll();
|
||||
CheckAndDispatchEvents(delta);
|
||||
}
|
||||
|
||||
public void ConnectToUrlEx(string url, double delayTime = 3,
|
||||
TlsOptions tlsClientOptions = null) {
|
||||
if (connectingTime >= 0) {
|
||||
return;
|
||||
}
|
||||
Error err = ConnectToUrl(url, tlsClientOptions);
|
||||
if (err != Error.Ok) {
|
||||
OnError?.Invoke("error", err);
|
||||
}
|
||||
connectingTime = delayTime;
|
||||
}
|
||||
|
||||
protected void CheckAndDispatchEvents(double delta) {
|
||||
var state = GetReadyState();
|
||||
switch (state) {
|
||||
case State.Open when !isConnected:
|
||||
isConnected = true;
|
||||
OnOpen?.Invoke("open", null);
|
||||
break;
|
||||
case State.Open:
|
||||
while (GetAvailablePacketCount() > 0) {
|
||||
byte[] packet = GetPacket();
|
||||
OnMessage?.Invoke(packet);
|
||||
if (WasStringPacket()) {
|
||||
OnText?.Invoke(packet.GetStringFromUtf8());
|
||||
} else {
|
||||
OnBinary?.Invoke(packet);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case State.Closed:
|
||||
connectingTime = double.NegativeInfinity;
|
||||
OnClose?.Invoke("closed", GetCloseCode(), GetCloseReason());
|
||||
isConnected = false;
|
||||
break;
|
||||
case State.Closing:
|
||||
// OnClose?.Invoke("closing");
|
||||
break;
|
||||
case State.Connecting:
|
||||
if (connectingTime >= 0) {
|
||||
connectingTime -= delta;
|
||||
} else if (connectingTime != double.NegativeInfinity){
|
||||
connectingTime = double.NegativeInfinity;
|
||||
Close();
|
||||
OnError?.Invoke("connectTimeOut", Error.Timeout);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// 可以在这里处理其他状态或错误
|
||||
OnError?.Invoke("error", "Unknown WebSocket state.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SendBinaryEx(byte[] data) {
|
||||
SendEx(data);
|
||||
}
|
||||
|
||||
public void SendJsonEx(Dictionary msg) {
|
||||
SendTextEx(Json.Stringify(msg));
|
||||
}
|
||||
|
||||
public Error SendTextEx(string message) {
|
||||
if (isConnected) {
|
||||
return SendText(message);
|
||||
} else {
|
||||
OnError?.Invoke("error", "Attempt to send on a closed connection.");
|
||||
return Error.ConnectionError;
|
||||
}
|
||||
}
|
||||
|
||||
public Error SendEx(byte[] message, WriteMode writeMode = WriteMode.Binary) {
|
||||
if (isConnected) {
|
||||
return Send(message, writeMode);
|
||||
} else {
|
||||
OnError?.Invoke("error", "Attempt to send on a closed connection.");
|
||||
return Error.ConnectionError;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
using System.Dynamic;
|
||||
using Godot;
|
||||
|
||||
class VirtualBoard {
|
||||
@ -11,12 +10,12 @@ class VirtualBoard {
|
||||
return arrayPos.X < 0 || arrayPos.X >= boradRows || arrayPos.Y < 0 || arrayPos.Y >= boradCols;
|
||||
}
|
||||
|
||||
public Node getNodeFromBoard(Vector2 arrayPos) {
|
||||
public Node GetNodeFromBoard(Vector2 arrayPos) {
|
||||
if (ArrPosOutOfRange(arrayPos)) return null;
|
||||
return nodesBorad[(int)arrayPos.X, (int)arrayPos.Y];
|
||||
}
|
||||
|
||||
public void setNodeToBoard(Vector2 arrayPos, Node node) {
|
||||
public void SetNodeToBoard(Vector2 arrayPos, Node node) {
|
||||
if (ArrPosOutOfRange(arrayPos)) return;
|
||||
nodesBorad[(int)arrayPos.X, (int)arrayPos.Y] = node;
|
||||
}
|
||||
@ -30,29 +29,29 @@ class VirtualBoard {
|
||||
if (ArrPosOutOfRange(newPos) || ArrPosOutOfRange(oldPos)) {
|
||||
return false;
|
||||
}
|
||||
if (getNodeFromBoard(newPos) != null) {
|
||||
if (GetNodeFromBoard(newPos) != null) {
|
||||
return false;
|
||||
}
|
||||
if (getNodeFromBoard(oldPos) is ChessPiece chessPiece) {
|
||||
if (GetNodeFromBoard(oldPos) is ChessPiece chessPiece) {
|
||||
chessPiece.movePos(newPos);
|
||||
}
|
||||
setNodeToBoard(newPos, getNodeFromBoard(oldPos));
|
||||
setNodeToBoard(oldPos, null);
|
||||
SetNodeToBoard(newPos, GetNodeFromBoard(oldPos));
|
||||
SetNodeToBoard(oldPos, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveNode(Vector2 arrayPos) {
|
||||
if (ArrPosOutOfRange(arrayPos)) return false;
|
||||
if (getNodeFromBoard(arrayPos) is ChessPiece chessPiece) {
|
||||
if (GetNodeFromBoard(arrayPos) is ChessPiece chessPiece) {
|
||||
chessPiece.QueueFree();
|
||||
}
|
||||
setNodeToBoard(arrayPos, null);
|
||||
SetNodeToBoard(arrayPos, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void InsertNode(ChessPiece chess, Vector2 arrayPos) {
|
||||
chess.Position = PosTrans.transArrToPix * arrayPos;
|
||||
setNodeToBoard(arrayPos, chess);
|
||||
SetNodeToBoard(arrayPos, chess);
|
||||
BoardRoot.AddChild(chess);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user