초기커밋
This commit is contained in:
124
GameServer/Contents/Room/Action/RoomAction.cs
Normal file
124
GameServer/Contents/Room/Action/RoomAction.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
public class RoomAction : EntityActionBase
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, Room> m_rooms = new();
|
||||
|
||||
public RoomAction(Player owner) : base(owner)
|
||||
{}
|
||||
|
||||
public override async Task<Result> onInit()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
var result = new Result();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void onClear()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private Room? getRoom(int room_id) => m_rooms.GetValueOrDefault(room_id);
|
||||
|
||||
public bool IsExist(int roomId)
|
||||
{
|
||||
return m_rooms.ContainsKey(roomId);
|
||||
}
|
||||
|
||||
public async Task<Result> setRoomFromDoc(RoomDoc docBase)
|
||||
{
|
||||
var result = new Result();
|
||||
string err_msg;
|
||||
|
||||
var owner = getOwner() as Player;
|
||||
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
||||
|
||||
var doc_attr = docBase.getAttrib<RoomAttrib>();
|
||||
if (null == doc_attr)
|
||||
{
|
||||
err_msg = $"Fail to get doc attrib : {nameof(RoomAttrib)} is null";
|
||||
result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
// db 에서 설정하는 것으로, 없으면 생성해야 한다.
|
||||
var room = getRoom(doc_attr.RoomId);
|
||||
if (null == room)
|
||||
{
|
||||
room = new Room(owner);
|
||||
m_rooms.TryAdd(doc_attr.RoomId, room);
|
||||
}
|
||||
|
||||
var room_attribute = room.getEntityAttribute<RoomAttribute>();
|
||||
if (null == room_attribute)
|
||||
{
|
||||
err_msg = $"Fail to get entity attribute : {nameof(RoomAttribute)} is null";
|
||||
result.setFail(ServerErrorCode.EntityAttributeIsNull, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
// doc 에서 직접 copy 된 경우 pk, sk 를 셋팅해야 한다.
|
||||
docBase.setCombinationKeyForPK(owner.getUserGuid());
|
||||
docBase.setCombinationKeyForSK(doc_attr.RoomId.ToString());
|
||||
var is_apply = docBase.onApplyPKSK();
|
||||
if (ServerErrorCode.Success != is_apply)
|
||||
{
|
||||
err_msg = $"Fail to copy to attribute from doc : invalid argument {nameof(setRoomFromDoc)}";
|
||||
result.setFail(is_apply, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
var is_success = room_attribute.copyEntityAttributeFromDoc(docBase);
|
||||
if (false == is_success)
|
||||
{
|
||||
err_msg = $"Fail to copy room attribute : to {nameof(RoomAttribute)} from {nameof(RoomDoc)}";
|
||||
result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public async Task<Result> notifyRoomsToClient()
|
||||
{
|
||||
var owner = getOwner() as Player;
|
||||
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
||||
|
||||
var notify_packet = new ClientToGame
|
||||
{
|
||||
Message = new ClientToGameMessage
|
||||
{
|
||||
OwnedRoomNoti = new ClientToGameMessage.Types.OwnedRoomNoti()
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var room in m_rooms)
|
||||
{
|
||||
var room_content_action = room.Value.getEntityAction<RoomContentAction>();
|
||||
|
||||
var data = room_content_action?.toRoomData4Client();
|
||||
if (null == data) continue;
|
||||
|
||||
notify_packet.Message.OwnedRoomNoti.RoomList.Add(data);
|
||||
}
|
||||
|
||||
GameServerApp.getServerLogic().onSendPacket(owner, notify_packet);
|
||||
|
||||
return await Task.FromResult(new Result());
|
||||
}
|
||||
}
|
||||
71
GameServer/Contents/Room/Action/RoomContentAction.cs
Normal file
71
GameServer/Contents/Room/Action/RoomContentAction.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
|
||||
public class RoomContentAction : EntityActionBase
|
||||
{
|
||||
public RoomContentAction(Room owner) : base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<Result> onInit()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
var result = new Result();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void onClear()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public RoomInfo? toRoomData4Client()
|
||||
{
|
||||
var attribute = getOwner().getEntityAttribute<RoomAttribute>();
|
||||
if (null == attribute)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var data = new RoomInfo
|
||||
{
|
||||
Id = attribute.RoomId,
|
||||
Owner = attribute.Owner,
|
||||
Name = attribute.Name,
|
||||
Description = attribute.Description
|
||||
};
|
||||
|
||||
foreach (var info in attribute.PropInfo)
|
||||
{
|
||||
var prop = new PropInfo();
|
||||
prop.AnchorGuid = info.Key;
|
||||
prop.TableId = info.Value.TableId;
|
||||
|
||||
data.PropList.Add(prop);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task<Result> notifyChangeRoomToClientAndServers()
|
||||
{
|
||||
// todo (sangyeob.kim) : 값이 변경되면, 해당 데이터를 참조하는 곳에 notify 를 해줘야 한다.
|
||||
// Client, 타 Servers , Building Contents 에서 관리하고 있는 Rooms
|
||||
// ....
|
||||
|
||||
return await Task.FromResult(new Result());
|
||||
}
|
||||
}
|
||||
95
GameServer/Contents/Room/DbQuery/DBQRoomReadAll.cs
Normal file
95
GameServer/Contents/Room/DbQuery/DBQRoomReadAll.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
public class DBQRoomReadAll : QueryExecutorBase
|
||||
{
|
||||
private string m_pk { get; set; } = string.Empty;
|
||||
private readonly string m_combination_key_for_pk;
|
||||
private readonly List<RoomDoc> m_to_read_room_docs = new();
|
||||
|
||||
public DBQRoomReadAll(string combinationKeyForPk) : base(nameof(DBQRoomReadAll))
|
||||
{
|
||||
m_combination_key_for_pk = combinationKeyForPk;
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리 직전에 준비해야 할 로직들을 작성한다.
|
||||
//=====================================================================================
|
||||
public override async Task<Result> onPrepareQuery()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var owner = getOwner();
|
||||
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
||||
|
||||
var doc = new RoomDoc();
|
||||
doc.setCombinationKeyForPK(m_combination_key_for_pk);
|
||||
|
||||
var error_code = doc.onApplyPKSK();
|
||||
if (error_code.isFail())
|
||||
{
|
||||
var err_msg = $"Failed to onApplyPKSK() !!! : {error_code.toBasicString()} - {toBasicString()}, {owner.toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
return result;
|
||||
}
|
||||
|
||||
m_pk = doc.getPK();
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// onPrepareQuery()를 성공할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public override async Task<Result> onQuery()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var owner = getOwner();
|
||||
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!!");
|
||||
|
||||
var db_connector = query_batch.getDynamoDbConnector();
|
||||
var query_config = db_connector.makeQueryConfigForReadByPKSK(m_pk);
|
||||
|
||||
(result, var read_docs) = await db_connector.simpleQueryDocTypesWithQueryOperationConfig<RoomDoc>(query_config, eventTid: query_batch.getTransId());
|
||||
if (result.isFail())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var room_action = owner.getEntityAction<RoomAction>();
|
||||
NullReferenceCheckHelper.throwIfNull(room_action, () => $"room_action is null !!!");
|
||||
|
||||
foreach (var read_doc in read_docs)
|
||||
{
|
||||
result = await room_action.setRoomFromDoc(read_doc);
|
||||
if (result.isFail()) return result;
|
||||
|
||||
m_to_read_room_docs.Add(read_doc);
|
||||
}
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리를 성공하고, doFnCommit()가 QueryResultType.NotCalledQueryFunc를 반환할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public override async Task onQueryResponseCommit() => await Task.CompletedTask;
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리를 실패하고, doFnRollback()가 QueryResultType.NotCalledQueryFunc를 반환할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public override async Task onQueryResponseRollback(Result errorResult) => await Task.CompletedTask;
|
||||
|
||||
private new Player? getOwner() => getQueryBatch()?.getLogActor() as Player;
|
||||
}
|
||||
77
GameServer/Contents/Room/DbQuery/DBQRoomReadTarget.cs
Normal file
77
GameServer/Contents/Room/DbQuery/DBQRoomReadTarget.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using ServerCommon;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
namespace GameServer.Contents.Room.DbQuery;
|
||||
|
||||
public class DBQRoomReadTarget : QueryExecutorBase
|
||||
{
|
||||
|
||||
private string m_pk { get; set; } = string.Empty;
|
||||
private string m_sk { get; set; }
|
||||
private readonly string m_combination_key_for_pk;
|
||||
private RoomDoc m_to_read_room_doc = new();
|
||||
|
||||
public DBQRoomReadTarget(string combinationKeyForPk, int room_id) : base(nameof(DBQRoomReadTarget))
|
||||
{
|
||||
m_combination_key_for_pk = combinationKeyForPk;
|
||||
m_sk = room_id.ToString();
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리 직전에 준비해야 할 로직들을 작성한다.
|
||||
//=====================================================================================
|
||||
public override async Task<Result> onPrepareQuery()
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var doc = new RoomDoc();
|
||||
doc.setCombinationKeyForPK(m_combination_key_for_pk);
|
||||
|
||||
var error_code = doc.onApplyPKSK();
|
||||
if (error_code.isFail())
|
||||
{
|
||||
var err_msg = $"Failed to onApplyPKSK() !!! : {error_code.toBasicString()} - {toBasicString()}";
|
||||
result.setFail(error_code, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
return result;
|
||||
}
|
||||
|
||||
m_pk = doc.getPK();
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// onPrepareQuery()를 성공할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public override async Task<Result> onQuery()
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var query_batch = getQueryBatch();
|
||||
NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!!");
|
||||
|
||||
var db_connector = query_batch.getDynamoDbConnector();
|
||||
var query_config = db_connector.makeQueryConfigForReadByPKSK(m_pk, m_sk);
|
||||
|
||||
(result, var read_doc) = await db_connector.simpleQueryDocTypeWithQueryOperationConfig<RoomDoc>(query_config, eventTid: query_batch.getTransId());
|
||||
if (result.isFail()) return result;
|
||||
|
||||
// todo (sangyeob.kim): DBQReadTarget에서 획득한 doc을 필요로 하는 action 으로 보내야 한다.
|
||||
// ....
|
||||
|
||||
m_to_read_room_doc = read_doc!;
|
||||
return await Task.FromResult(new Result());
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리를 성공하고, doFnCommit()가 QueryResultType.NotCalledQueryFunc를 반환할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public override async Task onQueryResponseCommit() => await Task.CompletedTask;
|
||||
|
||||
//=====================================================================================
|
||||
// DB 쿼리를 실패하고, doFnRollback()가 QueryResultType.NotCalledQueryFunc를 반환할 경우 호출된다.
|
||||
//=====================================================================================
|
||||
public override async Task onQueryResponseRollback(Result errorResult) => await Task.CompletedTask;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
|
||||
namespace GameServer.PacketHandler;
|
||||
|
||||
[PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_CHANGE_SCREEN_PAGE), typeof(ChangeScreenPagePacketHandler), typeof(GameLoginListener))]
|
||||
public class ChangeScreenPagePacketHandler : PacketRecvHandler
|
||||
{
|
||||
public static void send_S2C_ACK_CHANGE_SCREEN_PAGE(Player? owner, Result result, int screenPageNo)
|
||||
{
|
||||
var ack_packet = new ClientToGame
|
||||
{
|
||||
Response = new ClientToGameRes
|
||||
{
|
||||
ErrorCode = result.ErrorCode,
|
||||
AckChangeScreenPage = new ClientToGameRes.Types.GS2C_ACK_CHANGE_SCREEN_PAGE
|
||||
{
|
||||
ScreenPageNo = screenPageNo
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GameServerApp.getServerLogic().onSendPacket(owner!, ack_packet);
|
||||
}
|
||||
|
||||
public override async Task<Result> onProcessPacket(ISession entityWithSession, IMessage recvMessage)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
var result = new Result();
|
||||
string err_msg;
|
||||
|
||||
var player = entityWithSession as Player;
|
||||
NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!");
|
||||
|
||||
// 1. 기본 정보 체크
|
||||
var request = (recvMessage as ClientToGame)?.Request.ReqChangeScreenPage;
|
||||
if (null == request)
|
||||
{
|
||||
err_msg = $"Failed to get request type !!! : {nameof(ClientToGame.Request.ReqChangeScreenPage)}";
|
||||
result.setFail(ServerErrorCode.InvalidArgument, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
send_S2C_ACK_CHANGE_SCREEN_PAGE(player, result, -1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 2. room id 확인
|
||||
(result, var room_id) = checkCondition(player, request);
|
||||
if (result.ErrorCode != ServerErrorCode.Success)
|
||||
{
|
||||
send_S2C_ACK_CHANGE_SCREEN_PAGE(player, result, -1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 3. room screen page 수정
|
||||
var screen_page = InstanceRoomManager.Instance.changeScreenPage(room_id, request.IsCustom == BoolType.True, request.IsIncrease == BoolType.True, request.CustomPage);
|
||||
if (screen_page < 0)
|
||||
{
|
||||
err_msg = $"failed to change screen page!! : roomId[{room_id}]";
|
||||
result.setFail(ServerErrorCode.NotExistInstanceRoom, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
}
|
||||
|
||||
send_S2C_ACK_CHANGE_SCREEN_PAGE(player, result, screen_page);
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
private (Result result, string room_id) checkCondition(Player player, ClientToGameReq.Types.C2GS_REQ_CHANGE_SCREEN_PAGE request)
|
||||
{
|
||||
var result = new Result();
|
||||
string err_msg;
|
||||
|
||||
var location = player.getEntityAttribute<LocationAttribute>();
|
||||
NullReferenceCheckHelper.throwIfNull(location, () => $"location is null !!!");
|
||||
|
||||
// 1. room id 체크
|
||||
var room_id = location.CurrentIndunLocation.InstanceRoomId;
|
||||
if (string.IsNullOrEmpty(room_id))
|
||||
{
|
||||
err_msg = "Failed to change screen page!! : user is not in room";
|
||||
result.setFail(ServerErrorCode.InvalidArgument, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
send_S2C_ACK_CHANGE_SCREEN_PAGE(player, result, -1);
|
||||
return (result, string.Empty);
|
||||
}
|
||||
|
||||
// 2. 매개변수 체크
|
||||
if (request.IsCustom == BoolType.True && request.CustomPage < 0)
|
||||
{
|
||||
err_msg = $"Failed to change screen page!! : invalid custom page number - {request.CustomPage}";
|
||||
result.setFail(ServerErrorCode.InvalidArgument, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
send_S2C_ACK_CHANGE_SCREEN_PAGE(player, result, -1);
|
||||
return (result, string.Empty);
|
||||
}
|
||||
|
||||
return (result, room_id);
|
||||
}
|
||||
}
|
||||
44
GameServer/Contents/Room/Room.cs
Normal file
44
GameServer/Contents/Room/Room.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
|
||||
public class Room : EntityBase
|
||||
{
|
||||
public Room(Player parent)
|
||||
: base(EntityType.Room, parent)
|
||||
{
|
||||
onInit().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public sealed override async Task<Result> onInit()
|
||||
{
|
||||
var parent = getDirectParent();
|
||||
NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!");
|
||||
addEntityAttribute(new RoomAttribute(this, parent));
|
||||
|
||||
addEntityAction(new RoomContentAction(this));
|
||||
|
||||
return await base.onInit();
|
||||
}
|
||||
|
||||
public override string toBasicString()
|
||||
{
|
||||
return $"{this.getTypeName()} - {getRootParent().toBasicString()}";
|
||||
}
|
||||
|
||||
public override string toSummaryString()
|
||||
{
|
||||
return $"{this.getTypeName()} - {getRootParent().toBasicString()}";
|
||||
}
|
||||
}
|
||||
48
GameServer/Contents/Room/RoomCheat.cs
Normal file
48
GameServer/Contents/Room/RoomCheat.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Amazon.S3.Model;
|
||||
using GameServer.PacketHandler;
|
||||
using ServerCommon;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
namespace GameServer;
|
||||
|
||||
[ChatCommandAttribute("changepage", typeof(RoomCommandScreenPageChange), AuthAdminLevelType.Developer,
|
||||
AuthAdminLevelType.GmNormal, AuthAdminLevelType.GmSuper)]
|
||||
internal class RoomCommandScreenPageChange : ChatCommandBase
|
||||
{
|
||||
public override async Task invoke(Player player, string token, string[] args)
|
||||
{
|
||||
var result = new Result();
|
||||
if (args.Length < 2) return;
|
||||
|
||||
var isCustom = false;
|
||||
var isIncrease = false;
|
||||
var custromPage = -1;
|
||||
|
||||
if (false == bool.TryParse(args[0], out isCustom)) return;
|
||||
if (false == isCustom)
|
||||
{
|
||||
if (false == bool.TryParse(args[1], out isIncrease)) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(false == int.TryParse(args[1], out custromPage)) return;
|
||||
}
|
||||
|
||||
Log.getLogger().debug("Meeting Room Change Page");
|
||||
|
||||
var location = player.getEntityAttribute<LocationAttribute>();
|
||||
NullReferenceCheckHelper.throwIfNull(location, () => $"location is null !!! - {player.toBasicString()}");
|
||||
|
||||
var room_id = location.CurrentIndunLocation.InstanceRoomId;
|
||||
if (string.IsNullOrEmpty(room_id)) return;
|
||||
|
||||
var screen_page_no = InstanceRoomManager.Instance.changeScreenPage(room_id, isCustom, isIncrease, custromPage);
|
||||
Log.getLogger().debug($"Change Meeting Room Screen Page : {screen_page_no}");
|
||||
|
||||
if(screen_page_no < 0) result.setFail(ServerErrorCode.NotExistInstanceRoom, $"failed to change screen page!! : roomId[{room_id}]");
|
||||
|
||||
ChangeScreenPagePacketHandler.send_S2C_ACK_CHANGE_SCREEN_PAGE(player, result, screen_page_no);
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user