Debug_interface - success Server Communicate

This commit is contained in:
JackPWY 2025-04-03 12:19:36 +08:00
parent fdefca0555
commit ccc933d4f5
13 changed files with 743 additions and 446 deletions

View File

@ -1,4 +1,6 @@
//using installer.ViewModel; //ConfigDataFile.cs
//using installer.ViewModel;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;

View File

@ -1,4 +1,5 @@
using System.Collections.Concurrent; //Logger.cs
using System.Collections.Concurrent;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;

View File

@ -8,43 +8,43 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace debug_interface.Models namespace debug_interface.Models
{
//internal class MapCell
//{
//}
// 定义地图单元格的类型,根据游戏规则可包含障碍物、空地、草丛、资源、建筑等
public enum MapCellType
{ {
//internal class MapCell Obstacle, // 障碍物(例如地图边界或特定阻挡物)
//{ OpenLand, // 空地,角色可自由移动
//} Grass, // 草丛,可提供隐蔽效果(决战期后会消失)
// 定义地图单元格的类型,根据游戏规则可包含障碍物、空地、草丛、资源、建筑等 Resource, // 资源点(用于开采经济资源)
public enum MapCellType Building // 建筑(预留,例如兵营、农场等)
{
Obstacle, // 障碍物(例如地图边界或特定阻挡物)
OpenLand, // 空地,角色可自由移动
Grass, // 草丛,可提供隐蔽效果(决战期后会消失)
Resource, // 资源点(用于开采经济资源)
Building // 建筑(预留,例如兵营、农场等)
}
// 地图单元类,继承自 ObservableObject 便于数据绑定更新
public partial class MapCell : ObservableObject
{
// 地图中的行号0~49
[ObservableProperty]
private int cellX;
// 地图中的列号0~49
[ObservableProperty]
private int cellY;
// 当前单元格的类型
[ObservableProperty]
private MapCellType cellType;
// 该单元格显示时使用的颜色
[ObservableProperty]
private SolidColorBrush displayColor;
// 可选:显示的文字,例如建筑血量、资源进度等
[ObservableProperty]
private string displayText;
}
} }
// 地图单元类,继承自 ObservableObject 便于数据绑定更新
public partial class MapCell : ObservableObject
{
// 地图中的行号0~49
[ObservableProperty]
private int cellX;
// 地图中的列号0~49
[ObservableProperty]
private int cellY;
// 当前单元格的类型
[ObservableProperty]
private MapCellType cellType;
// 该单元格显示时使用的颜色
[ObservableProperty]
private SolidColorBrush displayColor;
// 可选:显示的文字,例如建筑血量、资源进度等
[ObservableProperty]
private string displayText;
}
}

View File

@ -0,0 +1,327 @@
//using System;
//using System.Threading.Tasks;
//using intaller.Data;
//using installer.Utils;
//using debug_interface.Utils;
//using THUAI8.Proto; // 确保这是你实际的Proto命名空间
//namespace debug_interface.Services
//{
// public class ServerCommunicationService
// {
// private readonly DebugConfig _config;
// private readonly Logger _logger;
// private Channel _channel;
// private DebugInterface.DebugInterfaceClient _client; // 使用你的Proto定义的客户端类
// private AsyncServerStreamingCall<MessageToClient> _responseStream;
// private bool _isConnected = false;
// private bool _isSpectatorMode = false;
// public ServerCommunicationService(
// IConfiguration configuration,
// Logger logger)
// {
// _config = configuration.GetSection("DebugConfig").Get<DebugConfig>();
// _logger = logger;
// }
// public bool IsConnected => _isConnected;
// public async Task ConnectToServer()
// {
// try
// {
// string ip = _config.Commands.IP;
// string port = _config.Commands.Port;
// long playerID = long.Parse(_config.Commands.PlayerID);
// long teamID = long.Parse(_config.Commands.TeamID);
// _logger.LogInfo($"正在连接到服务器 {ip}:{port}");
// _logger.LogInfo($"玩家ID: {playerID}, 队伍ID: {teamID}");
// if (playerID > 2023)
// {
// _isSpectatorMode = true;
// _logger.LogInfo("启用观察者模式");
// }
// // 创建连接地址
// string connect = $"{ip}:{port}";
// _logger.LogInfo($"创建gRPC通道: {connect}");
// // 创建Channel
// _channel = new Channel(connect, ChannelCredentials.Insecure);
// // 创建客户端
// _client = new DebugInterface.DebugInterfaceClient(_channel);
// _logger.LogInfo("创建gRPC客户端成功");
// // 创建玩家消息
// PlayerMsg playerMsg = new PlayerMsg();
// playerMsg.PlayerId = playerID;
// playerMsg.TeamId = teamID;
// // 如果不是观察者模式,设置附加信息
// if (!_isSpectatorMode)
// {
// // 设置Ship类型等信息
// int shipTypeID = int.Parse(_config.Commands.ShipType);
// ShipType shipType = (ShipType)shipTypeID;
// playerMsg.ShipType = shipType;
// _logger.LogInfo($"玩家舰船类型: {shipType}");
// }
// // 连接到服务器
// _logger.LogInfo("尝试连接服务器...");
// _responseStream = _client.AddPlayer(playerMsg);
// _logger.LogInfo("成功获取服务器响应流");
// _isConnected = true;
// _logger.LogInfo("成功连接到服务器!");
// // 开始接收服务器消息
// StartReceivingMessages();
// }
// catch (RpcException ex)
// {
// _isConnected = false;
// _logger.LogError($"gRPC连接错误: {ex.Status.Detail}");
// _logger.LogError($"错误状态码: {ex.Status.StatusCode}");
// }
// catch (Exception ex)
// {
// _isConnected = false;
// _logger.LogError($"连接失败: {ex.Message}");
// _logger.LogError($"异常类型: {ex.GetType().FullName}");
// if (ex.InnerException != null)
// {
// _logger.LogError($"内部异常: {ex.InnerException.Message}");
// }
// }
// }
// private async void StartReceivingMessages()
// {
// try
// {
// _logger.LogInfo("开始接收服务器消息...");
// while (_responseStream != null && await _responseStream.ResponseStream.MoveNext())
// {
// _logger.LogInfo("收到服务器消息");
// // 获取当前消息
// MessageToClient message = _responseStream.ResponseStream.Current;
// // 处理游戏状态
// switch (message.GameState)
// {
// case GameState.GameStart:
// _logger.LogInfo("游戏开始");
// ProcessGameStart(message);
// break;
// case GameState.GameRunning:
// _logger.LogInfo("游戏运行中");
// ProcessGameRunning(message);
// break;
// case GameState.GameEnd:
// _logger.LogInfo("游戏结束");
// ProcessGameEnd(message);
// break;
// }
// }
// _logger.LogInfo("服务器消息流结束");
// }
// catch (Exception ex)
// {
// _logger.LogError($"接收消息时发生错误: {ex.Message}");
// _isConnected = false;
// }
// }
// private void ProcessGameStart(MessageToClient message)
// {
// try
// {
// _logger.LogInfo("处理游戏开始消息");
// // 处理对象消息
// foreach (var obj in message.ObjMessage)
// {
// ProcessObjectMessage(obj);
// }
// // 处理全局消息
// _logger.LogInfo($"红方能量: {message.AllMessage.RedTeamEnergy}, 蓝方能量: {message.AllMessage.BlueTeamEnergy}");
// _logger.LogInfo($"红方血量: {message.AllMessage.RedHomeHp}, 蓝方血量: {message.AllMessage.BlueHomeHp}");
// _logger.LogInfo($"红方分数: {message.AllMessage.RedTeamScore}, 蓝方分数: {message.AllMessage.BlueTeamScore}");
// }
// catch (Exception ex)
// {
// _logger.LogError($"处理游戏开始消息时发生错误: {ex.Message}");
// }
// }
// private void ProcessGameRunning(MessageToClient message)
// {
// try
// {
// _logger.LogInfo("处理游戏运行消息");
// // 处理对象消息
// foreach (var obj in message.ObjMessage)
// {
// ProcessObjectMessage(obj);
// }
// // 处理全局消息
// _logger.LogInfo($"红方能量: {message.AllMessage.RedTeamEnergy}, 蓝方能量: {message.AllMessage.BlueTeamEnergy}");
// }
// catch (Exception ex)
// {
// _logger.LogError($"处理游戏运行消息时发生错误: {ex.Message}");
// }
// }
// private void ProcessGameEnd(MessageToClient message)
// {
// try
// {
// _logger.LogInfo("处理游戏结束消息");
// // 处理对象消息
// foreach (var obj in message.ObjMessage)
// {
// ProcessObjectMessage(obj);
// }
// // 处理全局消息
// _logger.LogInfo($"红方最终分数: {message.AllMessage.RedTeamScore}, 蓝方最终分数: {message.AllMessage.BlueTeamScore}");
// }
// catch (Exception ex)
// {
// _logger.LogError($"处理游戏结束消息时发生错误: {ex.Message}");
// }
// }
// private void ProcessObjectMessage(MessageOfObj obj)
// {
// try
// {
// switch (obj.MessageOfObjCase)
// {
// case MessageOfObj.MessageOfObjOneofCase.ShipMessage:
// _logger.LogInfo($"舰船位置: ({obj.ShipMessage.X}, {obj.ShipMessage.Y})");
// break;
// case MessageOfObj.MessageOfObjOneofCase.MapMessage:
// _logger.LogInfo("收到地图信息");
// break;
// case MessageOfObj.MessageOfObjOneofCase.BulletMessage:
// _logger.LogInfo($"子弹位置: ({obj.BulletMessage.X}, {obj.BulletMessage.Y})");
// break;
// // 可以添加其他消息类型的处理
// }
// }
// catch (Exception ex)
// {
// _logger.LogError($"处理对象消息时发生错误: {ex.Message}");
// }
// }
// public async Task DisconnectFromServer()
// {
// if (!_isConnected) return;
// try
// {
// _logger.LogInfo("正在断开服务器连接...");
// // 关闭响应流
// _responseStream?.Dispose();
// // 关闭通道
// if (_channel != null)
// {
// await _channel.ShutdownAsync();
// }
// _isConnected = false;
// _logger.LogInfo("已断开服务器连接");
// }
// catch (Exception ex)
// {
// _logger.LogError($"断开连接时发生错误: {ex.Message}");
// }
// }
// public async Task SendCommand(string command)
// {
// if (!_isConnected || _client == null)
// {
// _logger.LogWarning("无法发送命令:未连接到服务器");
// return;
// }
// try
// {
// _logger.LogInfo($"发送命令: {command}");
// // 这里需要根据实际的Proto定义来实现命令发送
// // 以下代码仅作为示例
// if (command.StartsWith("move"))
// {
// string direction = command.Substring(5);
// double angle = 0;
// switch (direction.ToLower())
// {
// case "up": angle = Math.PI; break;
// case "down": angle = 0; break;
// case "left": angle = Math.PI * 3 / 2; break;
// case "right": angle = Math.PI / 2; break;
// }
// MoveMsg moveMsg = new MoveMsg
// {
// PlayerId = long.Parse(_config.Commands.PlayerID),
// TeamId = long.Parse(_config.Commands.TeamID),
// Angle = angle,
// TimeInMilliseconds = 100
// };
// await _client.MoveAsync(moveMsg);
// _logger.LogInfo("移动命令已发送");
// }
// else if (command.StartsWith("attack"))
// {
// AttackMsg attackMsg = new AttackMsg
// {
// PlayerId = long.Parse(_config.Commands.PlayerID),
// TeamId = long.Parse(_config.Commands.TeamID),
// Angle = 0 // 或者根据需要设置角度
// };
// await _client.AttackAsync(attackMsg);
// _logger.LogInfo("攻击命令已发送");
// }
// // 添加其他命令的处理
// }
// catch (Exception ex)
// {
// _logger.LogError($"发送命令时发生错误: {ex.Message}");
// }
// }
// public void CleanUp()
// {
// _responseStream?.Dispose();
// _channel?.Dispose();
// }
// }
//}

View File

@ -1,30 +1,31 @@
//ViewModeBase.cs //ViewModeBase.cs
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using debug_interface.Models; using debug_interface.Models;
using Google.Protobuf; using Google.Protobuf;
using Protobuf;
using Grpc.Core; using Grpc.Core;
//using installer;
//using installer.Model; using installer.Model;
using installer.Data;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Logging; using Avalonia.Logging;
using System.Threading.Channels;
namespace debug_interface.ViewModels namespace debug_interface.ViewModels
{ {
public class ViewModelBase : ObservableObject public partial class ViewModelBase : ObservableObject
{ {
// 用于 UI 刷新的定时器Avalonia 的 DispatcherTimer // 用于 UI 刷新的定时器Avalonia 的 DispatcherTimer
private DispatcherTimer timerViewModel; private DispatcherTimer timerViewModel;
private int counterViewModelTest = 0; private int counterViewModelTest = 0;
// 使用 CommunityToolkit 的 ObservableProperty 自动实现 INotifyPropertyChanged // 使用 CommunityToolkit 的 ObservableProperty 自动实现 INotifyPropertyChanged
//[ObservableProperty] [ObservableProperty]
//private string title; private string title;
//private MapPatch testPatch; //private MapPatch testPatch;
//public MapPatch TestPatch //public MapPatch TestPatch
@ -48,286 +49,64 @@ namespace debug_interface.ViewModels
private long teamID; private long teamID;
//private ShipType shipType; //private ShipType shipType;
//private AvailableService.AvailableServiceClient? client; private AvailableService.AvailableServiceClient? client;
//private AsyncServerStreamingCall<MessageToClient>? responseStream; private AsyncServerStreamingCall<MessageToClient>? responseStream;
private bool isSpectatorMode = false; private bool isSpectatorMode = false;
private bool isPlaybackMode = false; private bool isPlaybackMode = false;
// 用于存储上次移动角度(示例,仅作为参考) // 用于存储上次移动角度(示例,仅作为参考)
private double lastMoveAngle; private double lastMoveAngle;
// 日志记录(保持你原来的 Logger 类) // 日志记录
//public Logger myLogger; //public Logger myLogger;
//public Logger lockGenerator; //public Logger lockGenerator;
// 以下定义各个操作的命令(基于 CommunityToolkit.Mvvm.Input 的 RelayCommand
public RelayCommand MoveUpCommand { get; }
public RelayCommand MoveDownCommand { get; }
public RelayCommand MoveLeftCommand { get; }
public RelayCommand MoveRightCommand { get; }
public RelayCommand MoveLeftUpCommand { get; }
public RelayCommand MoveRightUpCommand { get; }
public RelayCommand MoveLeftDownCommand { get; }
public RelayCommand MoveRightDownCommand { get; }
public RelayCommand AttackCommand { get; }
public RelayCommand RecoverCommand { get; }
public RelayCommand ProduceCommand { get; }
public RelayCommand ConstructCommand { get; }
public ViewModelBase() public ViewModelBase()
{ {
//Title = "THUAI8; title = "THUAI8";
// 读取配置(假设 ConfigData 类来自 installer.Data 命名空间) // 读取配置(假设 ConfigData 类来自 installer.Data 命名空间)
//var d = new installer.Data.ConfigData(); var d = new installer.Data.ConfigData();
//ip = d.Commands.IP; ip = d.Commands.IP;
//port = d.Commands.Port; port = d.Commands.Port;
//playerID = Convert.ToInt64(d.Commands.PlayerID); playerID = Convert.ToInt64(d.Commands.PlayerID);
//teamID = Convert.ToInt64(d.Commands.TeamID); teamID = Convert.ToInt64(d.Commands.TeamID);
//shipTypeID = Convert.ToInt32(d.Commands.ShipType); //shipTypeID = Convert.ToInt32(d.Commands.ShipType);
//string playbackFile = d.Commands.PlaybackFile; string playbackFile = d.Commands.PlaybackFile;
//double playbackSpeed = d.Commands.PlaybackSpeed; double playbackSpeed = d.Commands.PlaybackSpeed;
// 初始化日志记录器 //初始化日志记录器
//myLogger = LoggerProvider.FromFile(System.IO.Path.Combine(d.InstallPath, "Logs", $"Client.{teamID}.{playerID}.log")); //myLogger = LoggerProvider.FromFile(System.IO.Path.Combine(d.InstallPath, "Logs", $"Client.{teamID}.{playerID}.log"));
//lockGenerator = LoggerProvider.FromFile(System.IO.Path.Combine(d.InstallPath, "Logs", $"lock.{teamID}.{playerID}.log")); //lockGenerator = LoggerProvider.FromFile(System.IO.Path.Combine(d.InstallPath, "Logs", $"lock.{teamID}.{playerID}.log"));
// 初始化命令:这里仅示例了部分命令,其他命令同理
MoveUpCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//// 构造移动消息,这里采用 gRPC 的消息格式(注意:根据你的 proto 定义,字段名称可能有所不同)
//MoveMsg movemsg = new MoveMsg
//{
// character_id = playerID,
// team_id = teamID,
// angle = Math.PI,
// time_in_milliseconds = 50
//};
//lastMoveAngle = movemsg.angle;
//client.Move(movemsg);
});
MoveDownCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//MoveMsg movemsg = new MoveMsg
//{
// character_id = playerID,
// team_id = teamID,
// // 这里使用 0 表示“负零”
// angle = 0,
// time_in_milliseconds = 100
//};
//lastMoveAngle = movemsg.angle;
//client.Move(movemsg);
});
MoveLeftCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//MoveMsg movemsg = new MoveMsg
//{
// character_id = playerID,
// team_id = teamID,
// angle = Math.PI * 3 / 2,
// time_in_milliseconds = 100
//};
//lastMoveAngle = movemsg.angle;
//client.Move(movemsg);
});
MoveRightCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//MoveMsg movemsg = new MoveMsg
//{
// character_id = playerID,
// team_id = teamID,
// angle = Math.PI / 2,
// time_in_milliseconds = 100
//};
//lastMoveAngle = movemsg.angle;
//client.Move(movemsg);
});
MoveLeftUpCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//MoveMsg movemsg = new MoveMsg
//{
// character_id = playerID,
// team_id = teamID,
// angle = Math.PI * 5 / 4,
// time_in_milliseconds = 100
//};
//lastMoveAngle = movemsg.angle;
//client.Move(movemsg);
});
MoveRightUpCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//MoveMsg movemsg = new MoveMsg
//{
// character_id = playerID,
// team_id = teamID,
// angle = Math.PI * 3 / 4,
// time_in_milliseconds = 100
//};
//lastMoveAngle = movemsg.angle;
//client.Move(movemsg);
});
MoveLeftDownCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//MoveMsg movemsg = new MoveMsg
//{
// character_id = playerID,
// team_id = teamID,
// angle = Math.PI * 7 / 4,
// time_in_milliseconds = 100
//};
//lastMoveAngle = movemsg.angle;
//client.Move(movemsg);
});
MoveRightDownCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//MoveMsg movemsg = new MoveMsg
//{
// character_id = playerID,
// team_id = teamID,
// angle = Math.PI / 4,
// time_in_milliseconds = 100
//};
//lastMoveAngle = movemsg.angle;
//client.Move(movemsg);
});
AttackCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//AttackMsg attackMsg = new AttackMsg
//{
// character_id = playerID,
// team_id = teamID,
// attack_range = 50, // 示例值,根据实际情况修改
// attacked_character_id = 0 // 示例目标ID
//};
//client.Attack(attackMsg);
});
RecoverCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//RecoverMsg recoverMsg = new RecoverMsg
//{
// character_id = playerID,
// recovered_hp = 10, // 示例数值
// team_id = teamID
//};
//client.Recover(recoverMsg);
});
ProduceCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//// 此处 Produce 可使用对应的 gRPC 方法(例如 Equip 或其他你定义的生产逻辑)
//IDMsg idMsg = new IDMsg
//{
// character_id = playerID,
// team_id = teamID
//};
//client.Equip(idMsg);
});
ConstructCommand = new RelayCommand(() =>
{
//if (client == null || isSpectatorMode || isPlaybackMode)
//{
// myLogger.LogInfo("Client is null or in Spectator/Playback mode");
// return;
//}
//ConstructMsg constructMsg = new ConstructMsg
//{
// character_id = playerID,
// team_id = teamID,
// construction_type = ConstructionType.BARRACKS // 示例建筑类型
//};
//client.Construct(constructMsg);
});
// 使用 Avalonia 的 DispatcherTimer 定时刷新 UI // 使用 Avalonia 的 DispatcherTimer 定时刷新 UI
timerViewModel = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) }; timerViewModel = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };
timerViewModel.Tick += Refresh; timerViewModel.Tick += Refresh;
timerViewModel.Start(); timerViewModel.Start();
// 判断是否走回放模式 //判断是否走回放模式
//if (string.IsNullOrEmpty(d.Commands.PlaybackFile)) if (string.IsNullOrEmpty(d.Commands.PlaybackFile))
//{ {
// string[] comInfo = new string[] string[] comInfo = new string[]
// { {
// ip, ip,
// port, port,
// Convert.ToString(playerID), Convert.ToString(playerID),
// Convert.ToString(teamID), Convert.ToString(teamID),
// Convert.ToString(shipTypeID), Convert.ToString(0),
// }; };
// ConnectToServer(comInfo);
// OnReceive(); ConnectToServer(comInfo);
//} OnReceive();
//else }
//{ else
// myLogger.LogInfo($"PlaybackFile: {d.Commands.PlaybackFile}"); {
// Playback(d.Commands.PlaybackFile, playbackSpeed); //myLogger.LogInfo($"PlaybackFile: {d.Commands.PlaybackFile}");
//} //Playback(d.Commands.PlaybackFile, playbackSpeed);
}
} }
/// <summary> /// <summary>
@ -336,38 +115,38 @@ namespace debug_interface.ViewModels
/// <param name="comInfo">包含 ip、port、playerID、teamID、shipTypeID 的数组</param> /// <param name="comInfo">包含 ip、port、playerID、teamID、shipTypeID 的数组</param>
public void ConnectToServer(string[] comInfo) public void ConnectToServer(string[] comInfo)
{ {
//if (isPlaybackMode) return; if (isPlaybackMode) return;
//if (Convert.ToInt64(comInfo[2]) > 2023) if (Convert.ToInt64(comInfo[2]) > 2023)
//{ {
// isSpectatorMode = true; isSpectatorMode = true;
// myLogger.LogInfo("isSpectatorMode = true"); //myLogger.LogInfo("isSpectatorMode = true");
//} }
//if (comInfo.Length != 5) if (comInfo.Length != 5)
//{ {
// throw new Exception("Error Registration Information"); throw new Exception("Error Registration Information");
//} }
//string connect = $"{comInfo[0]}:{comInfo[1]}"; string connect = $"{comInfo[0]}:{comInfo[1]}";
//Channel channel = new Channel(connect, ChannelCredentials.Insecure); Channel channel = new Channel(connect, ChannelCredentials.Insecure);
//client = new AvailableService.AvailableServiceClient(channel); client = new AvailableService.AvailableServiceClient(channel);
//PlayerMsg playerMsg = new PlayerMsg(); CharacterMsg playerMsg = new CharacterMsg();
//playerID = Convert.ToInt64(comInfo[2]); playerID = Convert.ToInt64(comInfo[2]);
//playerMsg.PlayerId = playerID; playerMsg.CharacterId = playerID;
//if (!isSpectatorMode) if (!isSpectatorMode)
//{ {
// teamID = Convert.ToInt64(comInfo[3]); teamID = Convert.ToInt64(comInfo[3]);
// playerMsg.TeamId = teamID; playerMsg.TeamId = teamID;
// shipType = Convert.ToInt64(comInfo[4]) switch //shipType = Convert.ToInt64(comInfo[4]) switch
// { //{
// 0 => ShipType.NullShipType, // 0 => ShipType.NullShipType,
// 1 => ShipType.CivilianShip, // 1 => ShipType.CivilianShip,
// 2 => ShipType.MilitaryShip, // 2 => ShipType.MilitaryShip,
// 3 => ShipType.FlagShip, // 3 => ShipType.FlagShip,
// _ => ShipType.NullShipType // _ => ShipType.NullShipType
// }; //};
// playerMsg.ShipType = shipType; playerMsg.CharacterType = CharacterType.TangSeng;
//} }
//responseStream = client.AddPlayer(playerMsg); responseStream = client.AddCharacter(playerMsg);
} }
/// <summary> /// <summary>

View File

@ -4,8 +4,8 @@
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:debug_interface.Views" xmlns:local="clr-namespace:debug_interface.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:debug_interface.ViewModels" xmlns:vm="using:debug_interface.ViewModels"
x:Name="MainWindowElement" x:Name="MainWindowElement"
Title="THUAI8 调试界面" Title="THUAI8 调试界面"
@ -207,10 +207,7 @@
</Grid> </Grid>
<!-- 右侧区域 --> <!-- 右侧区域 -->
<Grid <Grid Grid.Column="1" Margin="2">
Grid.Column="1"
Margin="2"
>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@ -248,14 +245,14 @@
</StackPanel> </StackPanel>
<!-- 地图 --> <!-- 地图 -->
<Border <Border
Grid.Row="1" Grid.Row="1"
Margin="5" Margin="5"
BorderBrush="Gray" BorderBrush="Gray"
BorderThickness="1"> BorderThickness="1">
<local:MapView /> <local:MapView />
</Border> </Border>
</Grid> </Grid>
</Grid> </Grid>
</Window> </Window>

View File

@ -4,8 +4,8 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace debug_interface.Views namespace debug_interface.Views
{ {
//public partial class MainWindow : Window //public partial class MainWindow : Window
//{ //{
// public MainWindow() // public MainWindow()

View File

@ -9,11 +9,11 @@
xmlns:vm="using:debug_interface.ViewModels" xmlns:vm="using:debug_interface.ViewModels"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid> <Grid>
<!-- 使用空的Grid将在代码中填充 --> <!-- 使用空的Grid将在代码中填充 -->
<Grid Name="MapGrid" /> <Grid Name="MapGrid" />
<!-- Character Positions Canvas --> <!-- Character Positions Canvas -->
<Canvas Name="CharacterCanvas" Background="Transparent" /> <Canvas Name="CharacterCanvas" Background="Transparent" />
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -144,65 +144,159 @@ namespace debug_interface.Views
{ {
if (characterCanvas == null) return; if (characterCanvas == null) return;
//bool isRedTeam = color.Equals(Colors.Red);
//// 形状选择 - 红队使用圆形,蓝队使用正方形
//for (int i = 0; i < characters.Count; i++)
//{
// var character = characters[i];
// // 创建容器 - 用于定位
// var container = new Canvas
// {
// Width = 16,
// Height = 16,
// Tag = character
// };
// Control characterShape;
// if (isRedTeam)
// {
// // 红队使用圆形
// characterShape = new Ellipse
// {
// Width = 14,
// Height = 14,
// Fill = new SolidColorBrush(color) { Opacity = 0.7 },
// Stroke = Brushes.White,
// StrokeThickness = 1
// };
// }
// else
// {
// // 蓝队使用正方形
// characterShape = new Rectangle
// {
// Width = 14,
// Height = 14,
// Fill = new SolidColorBrush(color) { Opacity = 0.7 },
// Stroke = Brushes.White,
// StrokeThickness = 1
// };
// }
// // 添加形状到容器
// Canvas.SetLeft(characterShape, 1);
// Canvas.SetTop(characterShape, 1);
// container.Children.Add(characterShape);
// // 添加标识符 - 使用不同的方式标识队伍内部的角色
// var identifier = new TextBlock
// {
// Text = (i + 1).ToString(),
// FontSize = 8,
// Foreground = Brushes.White,
// HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
// VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
// };
// // 为标识符添加背景以增强可读性
// var textContainer = new Border
// {
// Child = identifier,
// Width = 14,
// Height = 14,
// Background = Brushes.Transparent,
// HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
// VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
// };
// Canvas.SetLeft(textContainer, 1);
// Canvas.SetTop(textContainer, 1);
// container.Children.Add(textContainer);
// // 添加工具提示,显示详细信息
// var tooltip = new ToolTip
// {
// // 使用角色的泛型属性,确保可以访问
// Content = new TextBlock { Text = $"{(isRedTeam ? "红队" : "蓝队")} 角色 {i + 1}" }
// };
// ToolTip.SetTip(container, tooltip);
// // 将容器添加到画布
// characterCanvas.Children.Add(container);
// characterElements[character.Name] = container;
// // 初始定位 - 稍后会更新
// Canvas.SetLeft(container, i*i * i*i);
// Canvas.SetTop(container, i*i * i * i );
//}
/////////////////////////////////////
/////////////////////////////////////
////////////////////////////////////
for (int i = 0; i < characters.Count; i++) for (int i = 0; i < characters.Count; i++)
{ {
var character = characters[i];
var id = color == Colors.Red ? $"red_{i}" : $"blue_{i}";
// 创建一个Grid作为容器包含边框和文本/图标
var grid = new Grid
{ {
Width = 15, var character = characters[i];
Height = 15, var id = color == Colors.Red ? $"red_{i}" : $"blue_{i}";
};
// 创建带颜色边框的圆形 // 创建一个Grid作为容器包含边框和文本/图标
var borderellipse = new Ellipse var grid = new Grid
{
Width = 15,
Height = 15,
Fill = new SolidColorBrush(Colors.White), // 白色背景
Stroke = new SolidColorBrush(color), // 队伍颜色边框
StrokeThickness = 2,
Tag = character.Name,
};
grid.Children.Add(borderellipse);
// ===== 选项1: 显示数字编号 =====
// 如果不需要数字编号,注释掉下面这段代码
//var textBlock = new TextBlock
//{
// Text = (i + 1).ToString(), // 使用编号(从1开始)
// HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
// VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
// FontSize = 8,
// Foreground = new SolidColorBrush(color), // 文本颜色与队伍颜色一致
// FontWeight = FontWeight.Bold,
//};
//grid.Children.Add(textBlock);
// 设置提示信息
ToolTip.SetTip(grid, character.Name);
// 设置初始位置
Canvas.SetLeft(grid, character.PosY * 15);
Canvas.SetTop(grid, character.PosX * 15);
characterCanvas.Children.Add(grid);
// 存储Grid到字典中
characterElements[id] = grid;
// 设置属性更改处理器
character.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(CharacterViewModel.PosX) || e.PropertyName == nameof(CharacterViewModel.PosY))
{ {
// 更新Grid的位置 Width = 15,
UpdateCharacterPosition(grid, character.PosX, character.PosY); Height = 15,
} };
};
// 创建带颜色边框的圆形
var borderellipse = new Ellipse
{
Width = 15,
Height = 15,
Fill = new SolidColorBrush(Colors.White), // 白色背景
Stroke = new SolidColorBrush(color), // 队伍颜色边框
StrokeThickness = 2,
Tag = character.Name,
};
grid.Children.Add(borderellipse);
// ===== 选项1: 显示数字编号 =====
// 如果不需要数字编号,注释掉下面这段代码
var textBlock = new TextBlock
{
Text = (i + 1).ToString(), // 使用编号(从1开始)
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
FontSize = 8,
Foreground = new SolidColorBrush(color), // 文本颜色与队伍颜色一致
FontWeight = FontWeight.Bold,
};
//grid.Children.Add(textBlock);
// 设置提示信息
ToolTip.SetTip(grid, character.Name);
// 设置初始位置
Canvas.SetLeft(grid, character.PosY * 15);
Canvas.SetTop(grid, character.PosX * 15);
characterCanvas.Children.Add(grid);
// 存储Grid到字典中
characterElements[id] = grid;
// 设置属性更改处理器
character.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(CharacterViewModel.PosX) || e.PropertyName == nameof(CharacterViewModel.PosY))
{
// 更新Grid的位置
UpdateCharacterPosition(grid, character.PosX, character.PosY);
}
};
}
} }
} }
@ -226,5 +320,77 @@ namespace debug_interface.Views
// When collection changes, refresh all characters for simplicity // When collection changes, refresh all characters for simplicity
RefreshCharacters(); RefreshCharacters();
} }
public void UpdateCharacterPosition(long characterId, int x, int y, bool isRedTeam, string name)
{
// 查找现有角色标记或创建新标记
var marker = FindCharacterMarker(characterId);
if (marker == null)
{
// 创建新角色标记
marker = new Ellipse
{
Width = 10,
Height = 10,
Fill = new SolidColorBrush(isRedTeam ? Colors.Red : Colors.Blue),
Tag = characterId
};
// 添加文本标签
var label1 = new TextBlock
{
Text = name,
FontSize = 8,
Foreground = new SolidColorBrush(Colors.White)
};
// 添加到画布
CharacterCanvas.Children.Add(marker);
CharacterCanvas.Children.Add(label1);
}
// 更新位置
double cellWidth = CharacterCanvas.Bounds.Width / 50;
double cellHeight = CharacterCanvas.Bounds.Height / 50;
Canvas.SetLeft(marker, y * cellWidth + cellWidth / 2 - marker.Width / 2);
Canvas.SetTop(marker, x * cellHeight + cellHeight / 2 - marker.Height / 2);
// 更新标签位置
var label = FindCharacterLabel(characterId);
if (label != null)
{
Canvas.SetLeft(label, y * cellWidth + cellWidth / 2 - label.Bounds.Width / 2);
Canvas.SetTop(label, x * cellHeight + cellHeight / 2 + marker.Height);
}
}
private Ellipse FindCharacterMarker(long characterId)
{
foreach (var child in CharacterCanvas.Children)
{
if (child is Ellipse ellipse && (long?)ellipse.Tag == characterId)
{
return ellipse;
}
}
return null;
}
private TextBlock FindCharacterLabel(long characterId)
{
var marker = FindCharacterMarker(characterId);
if (marker == null) return null;
int index = CharacterCanvas.Children.IndexOf(marker);
if (index >= 0 && index + 1 < CharacterCanvas.Children.Count &&
CharacterCanvas.Children[index + 1] is TextBlock label)
{
return label;
}
return null;
}
} }
} }

View File

@ -1,22 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<AssemblyName>debug_interface</AssemblyName> <AssemblyName>debug_interface</AssemblyName>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\logic\proto\proto.csproj" /> <AvaloniaResource Include="Assets\**" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<!-- 相对路径 -->
<Protobuf Include="..\..\..\dependency\proto\*.proto" GrpcServices="Client">
<Link>Protos\%(RecursiveDir)%(Filename)%(Extension)</Link>
</Protobuf>
</ItemGroup>
<ItemGroup> <ItemGroup>
<!-- 使用正斜杠 / 确保跨平台兼容 --> <!-- 使用正斜杠 / 确保跨平台兼容 -->
<Compile Include="..\..\..\installer\Data\ConfigFileData.cs" Link="Interact/ConfigFileData.cs"> <Compile Include="..\..\..\installer\Data\ConfigFileData.cs" Link="Interact/ConfigFileData.cs">
@ -28,28 +30,32 @@
<Error Condition="!Exists('../../installer/Model/Logger.cs')" /> <Error Condition="!Exists('../../installer/Model/Logger.cs')" />
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.5" />
<PackageReference Include="Avalonia.Desktop" Version="11.2.5" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.5" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.5" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.5">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Google.Protobuf" Version="3.30.2" />
<PackageReference Include="Grpc.Net.Client" Version="2.70.0" />
<PackageReference Include="Grpc.Tools" Version="2.71.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Utili\" /> <PackageReference Include="Avalonia" Version="11.2.5" />
</ItemGroup> <PackageReference Include="Avalonia.Desktop" Version="11.2.5" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.5" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.5" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.5">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Google.Protobuf" Version="3.30.2" />
<PackageReference Include="Grpc.Net.Client" Version="2.70.0" />
<PackageReference Include="Grpc.Tools" Version="2.71.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Utili\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\dependency\proto\Proto.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -5,6 +5,8 @@ VisualStudioVersion = 17.11.35219.272
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "debug_interface", "debug_interface.csproj", "{4F12E13F-A324-4E71-8003-DB2D5C8C4643}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "debug_interface", "debug_interface.csproj", "{4F12E13F-A324-4E71-8003-DB2D5C8C4643}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proto", "..\..\..\dependency\proto\Proto.csproj", "{B6C305C4-ACFA-467E-9408-9617886B7D4D}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -15,6 +17,10 @@ Global
{4F12E13F-A324-4E71-8003-DB2D5C8C4643}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F12E13F-A324-4E71-8003-DB2D5C8C4643}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F12E13F-A324-4E71-8003-DB2D5C8C4643}.Release|Any CPU.ActiveCfg = Release|Any CPU {4F12E13F-A324-4E71-8003-DB2D5C8C4643}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F12E13F-A324-4E71-8003-DB2D5C8C4643}.Release|Any CPU.Build.0 = Release|Any CPU {4F12E13F-A324-4E71-8003-DB2D5C8C4643}.Release|Any CPU.Build.0 = Release|Any CPU
{B6C305C4-ACFA-467E-9408-9617886B7D4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6C305C4-ACFA-467E-9408-9617886B7D4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6C305C4-ACFA-467E-9408-9617886B7D4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6C305C4-ACFA-467E-9408-9617886B7D4D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -7,14 +7,26 @@ namespace ClientTest
{ {
public static Task Main(string[] args) public static Task Main(string[] args)
{ {
if (!int.TryParse(args[0], out int characterId))
{
Console.WriteLine("Invalid CharacterId. Please provide a valid integer.");
return Task.CompletedTask;
}
if (!int.TryParse(args[1], out int teamId))
{
Console.WriteLine("Invalid TeamId. Please provide a valid integer.");
return Task.CompletedTask;
}
Thread.Sleep(3000); Thread.Sleep(3000);
Channel channel = new("127.0.0.1:8888", ChannelCredentials.Insecure); Channel channel = new("127.0.0.1:8888", ChannelCredentials.Insecure);
var client = new AvailableService.AvailableServiceClient(channel); var client = new AvailableService.AvailableServiceClient(channel);
CharacterMsg playerInfo = new() CharacterMsg playerInfo = new()
{ {
CharacterId = 0, CharacterId = characterId,
TeamId = 0, TeamId = teamId,
CharacterType = CharacterType.TangSeng CharacterType = teamId == 0 ? CharacterType.JiuLing : CharacterType.TangSeng,
SideFlag = 1 - teamId
}; };
var call = client.AddCharacter(playerInfo); var call = client.AddCharacter(playerInfo);
MoveMsg moveMsg = new() MoveMsg moveMsg = new()

View File

@ -0,0 +1 @@