using System.Collections;
using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
{
///
/// 建议使用一个类来存储常量,方便重用
///
public class ExampleGameModuleConstant
{
public static GameModuleDepend GameModuleDepend => _depends;
public const string ExampleGameModule = "fungame.example.gamemodule";
public const string ExampleMap = "fungame.example.gamemap";
public const string ExampleCharacter = "fungame.example.character";
public const string ExampleSkill = "fungame.example.skill";
public const string ExampleItem = "fungame.example.item";
private static readonly string[] Maps = [ExampleMap];
private static readonly string[] Characters = [ExampleCharacter];
private static readonly string[] Skills = [ExampleSkill];
private static readonly string[] Items = [ExampleItem];
private static readonly GameModuleDepend _depends = new(Maps, Characters, Skills, Items);
}
///
/// 模组:必须继承基类:
/// 继承事件接口并实现其方法来使模组生效。例如继承:
///
public class ExampleGameModule : GameModule, IGamingUpdateInfoEvent
{
public override string Name => ExampleGameModuleConstant.ExampleGameModule;
public override string Description => "My First GameModule";
public override string Version => "1.0.0";
public override string Author => "FunGamer";
public override string DefaultMap => GameModuleDepend.MapsDepend.Length > 0 ? GameModuleDepend.MapsDepend[0] : "";
public override GameModuleDepend GameModuleDepend => ExampleGameModuleConstant.GameModuleDepend;
public override RoomType RoomType => RoomType.Mix;
public ExampleGameModule()
{
// 构造函数中可以指定模组连接到哪个模组服务器。
// 如果你使用自己的,保持默认即可:删除下面两行,并将模组服务器的名称设置为与此模组的名称相同
IsConnectToOtherServerModule = true;
AssociatedServerModuleName = ExampleGameModuleConstant.ExampleGameModule;
}
protected Gaming? Instance;
protected Room room = General.HallInstance;
protected List users = [];
protected Dictionary characters = [];
public override void StartGame(Gaming instance, params object[] args)
{
Instance = instance;
// 取得房间玩家等信息
GamingEventArgs eventArgs = instance.EventArgs;
room = eventArgs.Room;
users = eventArgs.Users;
// 客户端做好准备后,等待服务器的消息通知,下面可以根据需求进一步处理
}
public override void StartUI(params object[] args)
{
// 如果你是一个WPF或者Winform项目,可以在这里启动你的界面
// 如果没有,则不需要重写此方法
}
public void GamingUpdateInfoEvent(object sender, GamingEventArgs e, Hashtable data)
{
// 在下方的Server示例中,服务器发来的data中,包含check字符串,因此客户端要主动发起确认连接的请求。
if (data.ContainsKey("info_type"))
{
// 反序列化得到指定key的值
string info_type = DataRequest.GetHashtableJsonObject(data, "info_type") ?? "";
if (info_type == "check")
{
Guid token = DataRequest.GetHashtableJsonObject(data, "connect_token");
// 发起连接确认请求
DataRequest request = Controller.NewDataRequest(GamingType.Connect);
// 传递参数
request.AddRequestData("username", ((Gaming)sender).CurrentUser.Username);
request.AddRequestData("connect_token", token);
if (request.SendRequest() == RequestResult.Success)
{
string msg = request.GetResult("msg") ?? "";
Controller.WriteLine(msg);
}
request.Dispose();
}
}
}
}
///
/// 模组服务器:必须继承基类:
/// 使用switch块分类处理 。
///
public class ExampleGameModuleServer : GameModuleServer
{
///
/// 注意:服务器模组的名称必须和模组名称相同。除非你指定了 和
///
public override string Name => ExampleGameModuleConstant.ExampleGameModule;
public override string Description => "My First GameModule";
public override string Version => "1.0.0";
public override string Author => "FunGamer";
public override string DefaultMap => GameModuleDepend.MapsDepend.Length > 0 ? GameModuleDepend.MapsDepend.First() : "";
public override GameModuleDepend GameModuleDepend => ExampleGameModuleConstant.GameModuleDepend;
protected Room Room = General.HallInstance;
protected List Users = [];
protected IServerModel? RoomMaster;
protected Dictionary All = [];
public override bool StartServer(string GameModule, Room Room, List Users, IServerModel RoomMasterServerModel, Dictionary ServerModels, params object[] Args)
{
// 将参数转为本地属性
this.Room = Room;
this.Users = Users;
RoomMaster = RoomMasterServerModel;
All = ServerModels;
// 创建一个线程执行Test()
TaskUtility.NewTask(Test).OnError(Controller.Error);
return true;
}
private readonly List ConnectedUser = [];
private readonly Dictionary UserData = [];
private async Task Test()
{
Controller.WriteLine("欢迎各位玩家进入房间 " + Room.Roomid + " 。");
// 通常,我们可以对客户端的连接状态进行确认,此方法展示如何确认客户端的连接
// 有两种确认的方式,1是服务器主动确认,2是客户端发起确认
// 在FunGame项目中,建议永远使用客户端主动发起请求,因为服务器主动发起的实现难度较高
// 下面的演示基于综合的两种情况:服务器主动发送通知,客户端收到后,发起确认
// UpdateInfo是一个灵活的类型。如果发送check字符串,意味着服务器要求客户端发送确认
Hashtable data = [];
data.Add("info_type", "check");
// 进阶示例:传递一个token,让客户端返回
Guid token = Guid.NewGuid();
data.Add("connect_token", token);
// 我们保存到字典UserData中,这样可以方便跨方法检查变量
foreach (string username in Users.Select(u => u.Username).Distinct())
{
if (UserData.TryGetValue(username, out Hashtable? value))
{
value.Add("connect_token", token);
}
else
{
UserData.Add(username, []);
UserData[username].Add("connect_token", token);
}
}
SendAllGamingMessage(GamingType.UpdateInfo, data);
// 新建一个线程等待所有玩家确认
while (true)
{
if (ConnectedUser.Count == Users.Count) break;
// 每200ms确认一次,不需要太频繁
await Task.Delay(200);
}
Controller.WriteLine("所有玩家都已经连接。");
}
public override Hashtable GamingMessageHandler(string username, GamingType type, Hashtable data)
{
Hashtable result = [];
switch (type)
{
case GamingType.Connect:
// 编写处理“连接”命令的逻辑
// 如果需要处理客户端传递的参数:获取与客户端约定好的参数key对应的值
string un = NetworkUtility.JsonDeserializeFromHashtable(data, "username") ?? "";
Guid token = NetworkUtility.JsonDeserializeFromHashtable(data, "connect_token");
if (un == username && UserData.TryGetValue(username, out Hashtable? value) && value != null && (value["connect_token"]?.Equals(token) ?? false))
{
ConnectedUser.Add(Users.Where(u => u.Username == username).First());
Controller.WriteLine(username + " 已经连接。");
}
else Controller.WriteLine(username + " 确认连接失败!");
break;
}
return result;
}
// === 下面是一些常用的工具方法,用于服务器给客户端发送消息,可以直接添加到你的项目中 === //
protected void SendAllGamingMessage(GamingType type, Hashtable data)
{
// 循环服务线程,向所有玩家发送局内消息
foreach (IServerModel s in All.Values)
{
if (s != null && s.Socket != null)
{
s.Send(s.Socket, SocketMessageType.Gaming, type, data);
}
}
}
protected void SendGamingMessage(string username, GamingType type, Hashtable data)
{
// 向指定玩家发送局内消息
IServerModel s = All[username];
if (s != null && s.Socket != null)
{
s.Send(s.Socket, SocketMessageType.Gaming, type, data);
}
}
protected void SendAll(SocketMessageType type, params object[] args)
{
// 循环服务线程,向所有玩家发送消息
foreach (IServerModel s in All.Values)
{
if (s != null && s.Socket != null)
{
s.Send(s.Socket, type, args);
}
}
}
protected void Send(string username, SocketMessageType type, params object[] args)
{
// 向指定玩家发送消息
IServerModel s = All[username];
if (s != null && s.Socket != null)
{
s.Send(s.Socket, type, args);
}
}
}
///
/// 地图:必须继承基类:
///
public class ExampleGameMap : GameMap
{
public override string Name => ExampleGameModuleConstant.ExampleMap;
public override string Description => "My First GameMap";
public override string Version => "1.0.0";
public override string Author => "FunGamer";
public override float Length => 12.0f;
public override float Width => 12.0f;
public override float Height => 6.0f;
public override float Size => 4.0f;
}
///
/// 角色:必须继承基类:
///
public class ExampleCharacterModule : CharacterModule
{
public override string Name => ExampleGameModuleConstant.ExampleCharacter;
public override string Description => "My First CharacterModule";
public override string Version => "1.0.0";
public override string Author => "FunGamer";
public override List Characters
{
get
{
List list = [];
// 构建一个你想要的角色
Character c = Factory.GetCharacter();
c.Name = "Oshima";
c.FirstName = "Shiya";
c.NickName = "OSM";
c.MagicType = MagicType.PurityNatural;
c.InitialHP = 30;
c.InitialSTR = 20;
c.InitialAGI = 10;
c.InitialINT = 5;
c.InitialATK = 100;
c.InitialDEF = 10;
list.Add(c);
return list;
}
}
}
///
/// 技能:必须继承基类:
///
public class ExampleSkillModule : SkillModule
{
public override string Name => ExampleGameModuleConstant.ExampleSkill;
public override string Description => "My First SkillModule";
public override string Version => "1.0.0";
public override string Author => "FunGamer";
public override List Skills
{
get
{
List list = [];
Skill s = Factory.GetSkill();
s.Name = "Example Skill";
s.Description = "技能应该在GameModule中继承实现,再自行构造。";
list.Add(s);
return list;
}
}
}
///
/// 物品:必须继承基类:
///
public class ExampleItemModule : ItemModule
{
public override string Name => ExampleGameModuleConstant.ExampleItem;
public override string Description => "My First ItemModule";
public override string Version => "1.0.0";
public override string Author => "FunGamer";
public override List- Items
{
get
{
List
- list = [];
Item i = Factory.GetItem();
i.Name = "Example Item";
i.Price = 20;
list.Add(i);
return list;
}
}
}
}