mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-11-07 17:36:07 +00:00
Add submodule MinecraftProtocolLibrary
This commit is contained in:
parent
87026e1bfb
commit
3f1de66af3
62 changed files with 1093 additions and 450 deletions
|
|
@ -6,6 +6,7 @@ using System.IO;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.EntityHandler;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Inventory.ItemPalettes;
|
||||
using MinecraftClient.Mapping;
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
case 0x02: await ReadData(1); await ReadNextString(); await ReadNextString(); await ReadData(4); break;
|
||||
case 0x03:
|
||||
string message = await ReadNextString();
|
||||
handler.OnTextReceived(new ChatMessage(message, protocolversion >= 72, 0, Guid.Empty)); break;
|
||||
await handler.OnTextReceivedAsync(new ChatMessage(message, protocolversion >= 72, 0, Guid.Empty)); break;
|
||||
case 0x04: await ReadData(16); break;
|
||||
case 0x05: await ReadData(6); await ReadNextItemSlot(); break;
|
||||
case 0x06: await ReadData(12); break;
|
||||
|
|
@ -196,7 +196,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
case 0xC9:
|
||||
string name = await ReadNextString(); bool online = await ReadNextByte() != 0x00; await ReadData(2);
|
||||
Guid FakeUUID = new(MD5.HashData(Encoding.UTF8.GetBytes(name)).Take(16).ToArray());
|
||||
if (online) { handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); } else { handler.OnPlayerLeave(FakeUUID); }
|
||||
if (online) { await handler.OnPlayerJoinAsync(new PlayerInfo(name, FakeUUID)); } else { await handler.OnPlayerLeaveAsync(FakeUUID); }
|
||||
break;
|
||||
case 0xCA: if (protocolversion >= 72) { await ReadData(9); } else await ReadData(3); break;
|
||||
case 0xCB:
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Commands;
|
||||
using MinecraftClient.Crypto;
|
||||
using MinecraftClient.EntityHandler;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Inventory.ItemPalettes;
|
||||
using MinecraftClient.Logger;
|
||||
|
|
@ -71,6 +72,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
internal const int MC_1_19_Version = 759;
|
||||
internal const int MC_1_19_2_Version = 760;
|
||||
|
||||
private ulong CurrentTick = 0;
|
||||
|
||||
private int autocomplete_transaction_id = 0;
|
||||
private readonly Dictionary<int, short> window_actions = new();
|
||||
private bool login_phase = true;
|
||||
|
|
@ -218,6 +221,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
while (await periodicTimer.WaitForNextTickAsync(CancelToken) && !CancelToken.IsCancellationRequested)
|
||||
{
|
||||
++CurrentTick;
|
||||
try
|
||||
{
|
||||
await handler.OnUpdate();
|
||||
|
|
@ -226,7 +230,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
if (Config.Logging.DebugMessages)
|
||||
{
|
||||
ConsoleIO.WriteLine($"{e.GetType().Name} when ticking: {e.Message}");
|
||||
ConsoleIO.WriteLine($"{e.GetType().Name} on tick: {e.Message}");
|
||||
if (e.StackTrace != null)
|
||||
ConsoleIO.WriteLine(e.StackTrace);
|
||||
}
|
||||
|
|
@ -314,7 +318,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
await SendPacket(PacketTypesOut.Pong, packetData);
|
||||
break;
|
||||
case PacketTypesIn.JoinGame:
|
||||
Task OnGameJoinedTask = handler.OnGameJoined();
|
||||
Task OnGameJoinedTask = handler.OnGameJoinedAsync();
|
||||
|
||||
int playerEntityID = await dataTypes.ReadNextIntAsync(packetData);
|
||||
handler.OnReceivePlayerEntityID(playerEntityID);
|
||||
|
|
@ -439,7 +443,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
else
|
||||
senderUUID = Guid.Empty;
|
||||
|
||||
handler.OnTextReceived(new(message, true, messageType, senderUUID));
|
||||
await handler.OnTextReceivedAsync(new(message, true, messageType, senderUUID));
|
||||
}
|
||||
else if (protocolVersion == MC_1_19_Version) // 1.19
|
||||
{
|
||||
|
|
@ -477,7 +481,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
|
||||
ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult);
|
||||
handler.OnTextReceived(chat);
|
||||
await handler.OnTextReceivedAsync(chat);
|
||||
}
|
||||
else // 1.19.1 +
|
||||
{
|
||||
|
|
@ -554,7 +558,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult);
|
||||
if (isOnlineMode && !chat.LacksSender())
|
||||
await Acknowledge(chat);
|
||||
handler.OnTextReceived(chat);
|
||||
await handler.OnTextReceivedAsync(chat);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.CombatEvent:
|
||||
|
|
@ -567,7 +571,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
await dataTypes.SkipNextVarIntAsync(packetData);
|
||||
|
||||
handler.OnPlayerKilled(
|
||||
await handler.OnPlayerKilledAsync(
|
||||
await dataTypes.ReadNextIntAsync(packetData),
|
||||
ChatParser.ParseText(await dataTypes.ReadNextStringAsync(packetData))
|
||||
);
|
||||
|
|
@ -578,7 +582,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
case PacketTypesIn.DeathCombatEvent:
|
||||
await dataTypes.SkipNextVarIntAsync(packetData);
|
||||
|
||||
handler.OnPlayerKilled(
|
||||
await handler.OnPlayerKilledAsync(
|
||||
await dataTypes.ReadNextIntAsync(packetData),
|
||||
ChatParser.ParseText(await dataTypes.ReadNextStringAsync(packetData))
|
||||
);
|
||||
|
|
@ -673,7 +677,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
await dataTypes.SkipNextLocationAsync(packetData); // Death location
|
||||
}
|
||||
}
|
||||
handler.OnRespawn();
|
||||
await handler.OnRespawnAsync();
|
||||
break;
|
||||
case PacketTypesIn.PlayerPositionAndLook:
|
||||
{
|
||||
|
|
@ -907,7 +911,21 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
colors = await dataTypes.ReadNextByteArrayAsync(packetData);
|
||||
}
|
||||
|
||||
handler.OnMapData(mapid, scale, trackingPosition, locked, icons, columnsUpdated, rowsUpdated, mapCoulmnX, mapRowZ, colors);
|
||||
MapData mapData = new()
|
||||
{
|
||||
MapId = mapid,
|
||||
Scale = scale,
|
||||
TrackingPosition = trackingPosition,
|
||||
Locked = locked,
|
||||
Icons = icons,
|
||||
ColumnsUpdated = columnsUpdated,
|
||||
RowsUpdated = rowsUpdated,
|
||||
MapCoulmnX = mapCoulmnX,
|
||||
MapRowZ = mapRowZ,
|
||||
Colors = colors,
|
||||
};
|
||||
|
||||
await handler.OnMapData(mapData);
|
||||
break;
|
||||
case PacketTypesIn.TradeList:
|
||||
if (protocolVersion >= MC_1_14_Version && handler.GetInventoryEnabled())
|
||||
|
|
@ -985,7 +1003,20 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
fadeout = await dataTypes.ReadNextIntAsync(packetData);
|
||||
}
|
||||
}
|
||||
handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json);
|
||||
|
||||
TitlePacket title = new()
|
||||
{
|
||||
Action = action2,
|
||||
TitleText = titletext,
|
||||
SubtitleText = subtitletext,
|
||||
ActionbarText = actionbartext,
|
||||
Stay = stay,
|
||||
FadeIn = fadein,
|
||||
FadeOut = fadeout,
|
||||
JsonText = json,
|
||||
};
|
||||
|
||||
await handler.OnTitle(title);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.MultiBlockChange:
|
||||
|
|
@ -1253,14 +1284,14 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature));
|
||||
await handler.OnPlayerJoinAsync(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature));
|
||||
break;
|
||||
case 0x01: //Update gamemode
|
||||
await handler.OnGamemodeUpdate(uuid, await dataTypes.ReadNextVarIntAsync(packetData));
|
||||
break;
|
||||
case 0x02: //Update latency
|
||||
int latency = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
handler.OnLatencyUpdate(uuid, latency); //Update latency;
|
||||
await handler.OnLatencyUpdate(uuid, latency); //Update latency;
|
||||
break;
|
||||
case 0x03: //Update display name
|
||||
if (await dataTypes.ReadNextBoolAsync(packetData))
|
||||
|
|
@ -1273,7 +1304,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
break;
|
||||
case 0x04: //Player Leave
|
||||
handler.OnPlayerLeave(uuid);
|
||||
await handler.OnPlayerLeaveAsync(uuid);
|
||||
break;
|
||||
default:
|
||||
//Unknown player list item type
|
||||
|
|
@ -1288,8 +1319,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
short ping = await dataTypes.ReadNextShortAsync(packetData);
|
||||
Guid FakeUUID = new(MD5.HashData(Encoding.UTF8.GetBytes(name)).Take(16).ToArray());
|
||||
if (online)
|
||||
handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID));
|
||||
else handler.OnPlayerLeave(FakeUUID);
|
||||
await handler.OnPlayerJoinAsync(new PlayerInfo(name, FakeUUID));
|
||||
else
|
||||
await handler.OnPlayerLeaveAsync(FakeUUID);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.TabComplete:
|
||||
|
|
@ -1344,7 +1376,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
string title = await dataTypes.ReadNextStringAsync(packetData);
|
||||
byte slots = await dataTypes.ReadNextByteAsync(packetData);
|
||||
Container inventory = new(windowID, inventoryType, ChatParser.ParseText(title));
|
||||
handler.OnInventoryOpen(windowID, inventory);
|
||||
await handler.OnInventoryOpenAsync(windowID, inventory);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1353,7 +1385,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
int windowType = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
string title = await dataTypes.ReadNextStringAsync(packetData);
|
||||
Container inventory = new(windowID, windowType, ChatParser.ParseText(title));
|
||||
handler.OnInventoryOpen(windowID, inventory);
|
||||
await handler.OnInventoryOpenAsync(windowID, inventory);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -1362,7 +1394,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
byte windowID = await dataTypes.ReadNextByteAsync(packetData);
|
||||
lock (window_actions) { window_actions[windowID] = 0; }
|
||||
handler.OnInventoryClose(windowID);
|
||||
await handler.OnInventoryCloseAsync(windowID);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.WindowItems:
|
||||
|
|
@ -1395,7 +1427,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (protocolVersion >= MC_1_17_1_Version) // Carried Item - 1.17.1 and above
|
||||
await dataTypes.ReadNextItemSlotAsync(packetData, itemPalette);
|
||||
|
||||
handler.OnWindowItems(windowId, inventorySlots, stateId);
|
||||
await handler.OnWindowItemsAsync(windowId, inventorySlots, stateId);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.WindowProperty:
|
||||
|
|
@ -1403,7 +1435,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
short propertyId = await dataTypes.ReadNextShortAsync(packetData);
|
||||
short propertyValue = await dataTypes.ReadNextShortAsync(packetData);
|
||||
|
||||
handler.OnWindowProperties(containerId, propertyId, propertyValue);
|
||||
await handler.OnWindowPropertiesAsync(containerId, propertyId, propertyValue);
|
||||
break;
|
||||
case PacketTypesIn.SetSlot:
|
||||
if (handler.GetInventoryEnabled())
|
||||
|
|
@ -1414,7 +1446,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
stateId = await dataTypes.ReadNextVarIntAsync(packetData); // State ID - 1.17.1 and above
|
||||
short slotID = await dataTypes.ReadNextShortAsync(packetData);
|
||||
Item? item = await dataTypes.ReadNextItemSlotAsync(packetData, itemPalette);
|
||||
handler.OnSetSlot(windowID, slotID, item, stateId);
|
||||
await handler.OnSetSlotAsync(windowID, slotID, item, stateId);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.WindowConfirmation:
|
||||
|
|
@ -1452,7 +1484,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (handler.GetEntityHandlingEnabled())
|
||||
{
|
||||
Entity entity = await dataTypes.ReadNextEntity(packetData, entityPalette, false);
|
||||
handler.OnSpawnEntity(entity);
|
||||
await handler.OnSpawnEntity(entity);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.EntityEquipment:
|
||||
|
|
@ -1469,14 +1501,14 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
hasNext = bitsData >> 7 == 1;
|
||||
int slot2 = bitsData >> 1;
|
||||
Item? item = await dataTypes.ReadNextItemSlotAsync(packetData, itemPalette);
|
||||
handler.OnEntityEquipment(entityid, slot2, item);
|
||||
await handler.OnEntityEquipment(entityid, slot2, item);
|
||||
} while (hasNext);
|
||||
}
|
||||
else
|
||||
{
|
||||
int slot2 = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
Item? item = await dataTypes.ReadNextItemSlotAsync(packetData, itemPalette);
|
||||
handler.OnEntityEquipment(entityid, slot2, item);
|
||||
await handler.OnEntityEquipment(entityid, slot2, item);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -1487,7 +1519,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
// packet before 1.15 has metadata at the end
|
||||
// this is not handled in dataTypes.ReadNextEntity()
|
||||
// we are simply ignoring leftover data in packet
|
||||
handler.OnSpawnEntity(entity);
|
||||
await handler.OnSpawnEntity(entity);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.SpawnPlayer:
|
||||
|
|
@ -1503,17 +1535,17 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
Location EntityLocation = new(X, Y, Z);
|
||||
|
||||
handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch);
|
||||
await handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.EntityEffect:
|
||||
if (handler.GetEntityHandlingEnabled())
|
||||
{
|
||||
int entityid = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
Effects effect = Effects.Speed;
|
||||
EffectType effectType = EffectType.Speed;
|
||||
int effectId = protocolVersion >= MC_1_18_2_Version ?
|
||||
await dataTypes.ReadNextVarIntAsync(packetData) : await dataTypes.ReadNextByteAsync(packetData);
|
||||
if (Enum.TryParse(effectId.ToString(), out effect))
|
||||
if (Enum.TryParse(effectId.ToString(), out effectType))
|
||||
{
|
||||
int amplifier = await dataTypes.ReadNextByteAsync(packetData);
|
||||
int duration = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
|
|
@ -1529,7 +1561,19 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
factorCodec = await dataTypes.ReadNextNbtAsync(packetData);
|
||||
}
|
||||
|
||||
handler.OnEntityEffect(entityid, effect, amplifier, duration, flags, hasFactorData, factorCodec);
|
||||
Effect effect = new()
|
||||
{
|
||||
Type = effectType,
|
||||
EffectLevel = amplifier,
|
||||
StartTick = CurrentTick,
|
||||
DurationInTick = duration,
|
||||
IsFromBeacon = (flags & 0x01) > 0,
|
||||
ShowParticles = (flags & 0x02) > 0,
|
||||
ShowIcon = (flags & 0x04) > 0,
|
||||
FactorData = factorCodec,
|
||||
};
|
||||
|
||||
await handler.OnEntityEffect(entityid, effect);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -1544,7 +1588,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
entityList[i] = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
}
|
||||
handler.OnDestroyEntities(entityList);
|
||||
await handler.OnDestroyEntities(entityList);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.EntityPosition:
|
||||
|
|
@ -1558,7 +1602,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
DeltaX /= 128 * 32;
|
||||
DeltaY /= 128 * 32;
|
||||
DeltaZ /= 128 * 32;
|
||||
handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
|
||||
await handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.EntityPositionAndRotation:
|
||||
|
|
@ -1574,7 +1618,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
DeltaX /= 128 * 32;
|
||||
DeltaY /= 128 * 32;
|
||||
DeltaZ /= 128 * 32;
|
||||
handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
|
||||
await handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.EntityProperties:
|
||||
|
|
@ -1609,7 +1653,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x);
|
||||
keys.Add(_key, _value);
|
||||
}
|
||||
handler.OnEntityProperties(EntityID, keys);
|
||||
await handler.OnEntityProperties(EntityID, keys);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.EntityMetadata:
|
||||
|
|
@ -1641,20 +1685,20 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
int entityId = await dataTypes.ReadNextIntAsync(packetData);
|
||||
byte status = await dataTypes.ReadNextByteAsync(packetData);
|
||||
handler.OnEntityStatus(entityId, status);
|
||||
await handler.OnEntityStatus(entityId, status);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.TimeUpdate:
|
||||
long WorldAge = await dataTypes.ReadNextLongAsync(packetData);
|
||||
long TimeOfday = await dataTypes.ReadNextLongAsync(packetData);
|
||||
handler.OnTimeUpdate(WorldAge, TimeOfday);
|
||||
await handler.OnTimeUpdate(WorldAge, TimeOfday);
|
||||
break;
|
||||
case PacketTypesIn.SystemChat:
|
||||
string systemMessage = await dataTypes.ReadNextStringAsync(packetData);
|
||||
int msgType = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
if (msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)
|
||||
break;
|
||||
handler.OnTextReceived(new(systemMessage, true, msgType, Guid.Empty, true));
|
||||
await handler.OnTextReceivedAsync(new(systemMessage, true, msgType, Guid.Empty, true));
|
||||
break;
|
||||
case PacketTypesIn.EntityTeleport:
|
||||
if (handler.GetEntityHandlingEnabled())
|
||||
|
|
@ -1666,7 +1710,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
byte EntityYaw = await dataTypes.ReadNextByteAsync(packetData);
|
||||
byte EntityPitch = await dataTypes.ReadNextByteAsync(packetData);
|
||||
bool OnGround = await dataTypes.ReadNextBoolAsync(packetData);
|
||||
handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround);
|
||||
await handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround);
|
||||
}
|
||||
break;
|
||||
case PacketTypesIn.UpdateHealth:
|
||||
|
|
@ -1677,13 +1721,13 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
else
|
||||
food = await dataTypes.ReadNextShortAsync(packetData);
|
||||
await dataTypes.SkipNextFloatAsync(packetData); // Food Saturation
|
||||
handler.OnUpdateHealth(health, food);
|
||||
await handler.OnUpdateHealth(health, food);
|
||||
break;
|
||||
case PacketTypesIn.SetExperience:
|
||||
float experiencebar = await dataTypes.ReadNextFloatAsync(packetData);
|
||||
int level = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
int totalexperience = await dataTypes.ReadNextVarIntAsync(packetData);
|
||||
handler.OnSetExperience(experiencebar, level, totalexperience);
|
||||
await handler.OnSetExperience(experiencebar, level, totalexperience);
|
||||
break;
|
||||
case PacketTypesIn.Explosion:
|
||||
Location explosionLocation = new(await dataTypes.ReadNextFloatAsync(packetData), await dataTypes.ReadNextFloatAsync(packetData), await dataTypes.ReadNextFloatAsync(packetData));
|
||||
|
|
@ -1700,11 +1744,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
float playerVelocityY = await dataTypes.ReadNextFloatAsync(packetData);
|
||||
float playerVelocityZ = await dataTypes.ReadNextFloatAsync(packetData);
|
||||
|
||||
handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount);
|
||||
await handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount);
|
||||
break;
|
||||
case PacketTypesIn.HeldItemChange:
|
||||
byte slot = await dataTypes.ReadNextByteAsync(packetData);
|
||||
handler.OnHeldItemChange(slot);
|
||||
await handler.OnHeldItemChange(slot);
|
||||
break;
|
||||
case PacketTypesIn.ScoreboardObjective:
|
||||
string objectivename = await dataTypes.ReadNextStringAsync(packetData);
|
||||
|
|
@ -1771,6 +1815,15 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ulong GetCurrentTick()
|
||||
{
|
||||
return CurrentTick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the server, cancel network reading.
|
||||
/// </summary>
|
||||
|
|
@ -1805,8 +1858,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
if (handler.GetNetworkPacketCaptureEnabled())
|
||||
{
|
||||
List<byte> clone = packetData.ToList();
|
||||
handler.OnNetworkPacket(packetID, clone, login_phase, false);
|
||||
await handler.OnNetworkPacketAsync(packetID, packetData.ToArray(), login_phase, false);
|
||||
}
|
||||
// log.Info("[C -> S] Sending packet " + packetID + " > " + dataTypes.ByteArrayToString(packetData.ToArray()));
|
||||
|
||||
|
|
@ -2248,7 +2300,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
/// <param name="acknowledgment">Message acknowledgment</param>
|
||||
/// <returns>True if properly sent</returns>
|
||||
public async Task<bool> SendMessageAcknowledgment(LastSeenMessageList.Acknowledgment acknowledgment)
|
||||
public async Task<bool> SendMessageAckAsync(LastSeenMessageList.Acknowledgment acknowledgment)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -2279,7 +2331,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
lastReceivedMessage = null;
|
||||
|
||||
if (pendingAcknowledgments++ > 64)
|
||||
await SendMessageAcknowledgment(ConsumeAcknowledgment());
|
||||
await SendMessageAckAsync(ConsumeAcknowledgment());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.EntityHandler;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Logger;
|
||||
using MinecraftClient.Mapping;
|
||||
|
|
@ -53,18 +54,18 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="packetData">A copy of Packet Data</param>
|
||||
/// <param name="isLogin">The packet is login phase or playing phase</param>
|
||||
/// <param name="isInbound">The packet is received from server or sent by client</param>
|
||||
void OnNetworkPacket(int packetID, List<byte> packetData, bool isLogin, bool isInbound);
|
||||
Task OnNetworkPacketAsync(int packetID, byte[] packetData, bool isLogin, bool isInbound);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a server was successfully joined
|
||||
/// </summary>
|
||||
Task OnGameJoined();
|
||||
Task OnGameJoinedAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Received chat/system message from the server
|
||||
/// </summary>
|
||||
/// <param name="message">Message received</param>
|
||||
public void OnTextReceived(ChatMessage message);
|
||||
Task OnTextReceivedAsync(ChatMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Will be called every animations of the hit and place block
|
||||
|
|
@ -90,7 +91,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <summary>
|
||||
/// This method is called when the protocol handler receives a title
|
||||
/// </summary>
|
||||
void OnTitle(int action, string titletext, string subtitletext, string actionbartext, int fadein, int stay, int fadeout, string json);
|
||||
Task OnTitle(TitlePacket title);
|
||||
|
||||
/// <summary>
|
||||
/// Called when receiving a connection keep-alive from the server
|
||||
|
|
@ -116,36 +117,36 @@ namespace MinecraftClient.Protocol
|
|||
/// <summary>
|
||||
/// Called when an inventory is opened
|
||||
/// </summary>
|
||||
void OnInventoryOpen(int inventoryID, Container inventory);
|
||||
Task OnInventoryOpenAsync(int inventoryID, Container inventory);
|
||||
|
||||
/// <summary>
|
||||
/// Called when an inventory is closed
|
||||
/// </summary>
|
||||
void OnInventoryClose(int inventoryID);
|
||||
Task OnInventoryCloseAsync(int inventoryID);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player respawns, which happens on login, respawn and world change.
|
||||
/// </summary>
|
||||
void OnRespawn();
|
||||
Task OnRespawnAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a new player joins the game
|
||||
/// </summary>
|
||||
/// <param name="player">player info</param>
|
||||
public void OnPlayerJoin(PlayerInfo player);
|
||||
Task OnPlayerJoinAsync(PlayerInfo player);
|
||||
|
||||
/// <summary>
|
||||
/// This method is called when a player has left the game
|
||||
/// </summary>
|
||||
/// <param name="uuid">UUID of the player</param>
|
||||
void OnPlayerLeave(Guid uuid);
|
||||
Task OnPlayerLeaveAsync(Guid uuid);
|
||||
|
||||
/// <summary>
|
||||
/// This method is called when a player has been killed by another entity
|
||||
/// </summary>
|
||||
/// <param name="killerEntityId">Killer's entity if</param>
|
||||
/// <param name="chatMessage">message sent in chat when player is killed</param>
|
||||
void OnPlayerKilled(int killerEntityId, string chatMessage);
|
||||
Task OnPlayerKilledAsync(int killerEntityId, string chatMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the server sets the new location for the player
|
||||
|
|
@ -161,7 +162,7 @@ namespace MinecraftClient.Protocol
|
|||
void OnConnectionLost(ChatBot.DisconnectReason reason, string message);
|
||||
|
||||
/// <summary>
|
||||
/// Called ~10 times per second (10 ticks per second)
|
||||
/// Called ~20 times per second (20 ticks per second)
|
||||
/// Useful for updating bots in other parts of the program
|
||||
/// </summary>
|
||||
Task OnUpdate();
|
||||
|
|
@ -171,14 +172,14 @@ namespace MinecraftClient.Protocol
|
|||
/// </summary>
|
||||
/// <param name="channel">The channel to register.</param>
|
||||
/// <param name="bot">The bot to register the channel for.</param>
|
||||
Task RegisterPluginChannel(string channel, ChatBot bot);
|
||||
Task RegisterPluginChannelAsync(string channel, ChatBot bot);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the given plugin channel for the given bot.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to unregister.</param>
|
||||
/// <param name="bot">The bot to unregister the channel for.</param>
|
||||
Task UnregisterPluginChannel(string channel, ChatBot bot);
|
||||
Task UnregisterPluginChannelAsync(string channel, ChatBot bot);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a plugin channel packet to the server.
|
||||
|
|
@ -188,7 +189,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="data">The payload for the packet.</param>
|
||||
/// <param name="sendEvenIfNotRegistered">Whether the packet should be sent even if the server or the client hasn't registered it yet.</param>
|
||||
/// <returns>Whether the packet was sent: true if it was sent, false if there was a connection error or it wasn't registered.</returns>
|
||||
Task<bool> SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false);
|
||||
Task<bool> SendPluginChannelMessageAsync(string channel, byte[] data, bool sendEvenIfNotRegistered = false);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a plugin channel message was sent from the server.
|
||||
|
|
@ -201,7 +202,7 @@ namespace MinecraftClient.Protocol
|
|||
/// Called when an entity has spawned
|
||||
/// </summary>
|
||||
/// <param name="entity">Spawned entity</param>
|
||||
void OnSpawnEntity(Entity entity);
|
||||
Task OnSpawnEntity(Entity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Called when an entity has spawned
|
||||
|
|
@ -209,7 +210,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="entityid">Entity id</param>
|
||||
/// <param name="slot">Equipment slot. 0: main hand, 1: off hand, 2–5: armor slot (2: boots, 3: leggings, 4: chestplate, 5: helmet)/param>
|
||||
/// <param name="item">Item/param>
|
||||
void OnEntityEquipment(int entityid, int slot, Item? item);
|
||||
Task OnEntityEquipment(int entityid, int slot, Item? item);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a player spawns or enters the client's render distance
|
||||
|
|
@ -219,13 +220,13 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="location">Entity location</param>
|
||||
/// <param name="yaw">Player head yaw</param>
|
||||
/// <param name="pitch">Player head pitch</param>
|
||||
void OnSpawnPlayer(int entityID, Guid uuid, Location location, byte yaw, byte pitch);
|
||||
Task OnSpawnPlayer(int entityID, Guid uuid, Location location, byte yaw, byte pitch);
|
||||
|
||||
/// <summary>
|
||||
/// Called when entities have despawned
|
||||
/// </summary>
|
||||
/// <param name="EntityID">List of Entity ID that have despawned</param>
|
||||
void OnDestroyEntities(int[] EntityID);
|
||||
Task OnDestroyEntities(int[] EntityID);
|
||||
|
||||
/// <summary>
|
||||
/// Called when an entity moved by coordinate offset
|
||||
|
|
@ -235,7 +236,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="Dy">Y offset</param>
|
||||
/// <param name="Dz">Z offset</param>
|
||||
/// <param name="onGround">TRUE if on ground</param>
|
||||
void OnEntityPosition(int entityID, Double dx, Double dy, Double dz, bool onGround);
|
||||
Task OnEntityPosition(int entityID, Double dx, Double dy, Double dz, bool onGround);
|
||||
|
||||
/// <summary>
|
||||
/// Called when an entity moved to fixed coordinates
|
||||
|
|
@ -245,28 +246,28 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="Dy">Y</param>
|
||||
/// <param name="Dz">Z</param>
|
||||
/// <param name="onGround">TRUE if on ground</param>
|
||||
void OnEntityTeleport(int entityID, Double x, Double y, Double z, bool onGround);
|
||||
Task OnEntityTeleport(int entityID, Double x, Double y, Double z, bool onGround);
|
||||
|
||||
/// <summary>
|
||||
/// Called when additional properties have been received for an entity
|
||||
/// </summary>
|
||||
/// <param name="EntityID">Entity ID</param>
|
||||
/// <param name="prop">Dictionary of properties</param>
|
||||
void OnEntityProperties(int entityID, Dictionary<string, Double> prop);
|
||||
Task OnEntityProperties(int entityID, Dictionary<string, Double> prop);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the status of an entity have been changed
|
||||
/// </summary>
|
||||
/// <param name="entityID">Entity ID</param>
|
||||
/// <param name="status">Status ID</param>
|
||||
void OnEntityStatus(int entityID, byte status);
|
||||
Task OnEntityStatus(int entityID, byte status);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the world age has been updated
|
||||
/// </summary>
|
||||
/// <param name="WorldAge">World age</param>
|
||||
/// <param name="TimeOfDay">Time of Day</param>
|
||||
void OnTimeUpdate(long worldAge, long timeOfDay);
|
||||
Task OnTimeUpdate(long worldAge, long timeOfDay);
|
||||
|
||||
/// <summary>
|
||||
/// When received window properties from server.
|
||||
|
|
@ -275,7 +276,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="inventoryID">Inventory ID</param>
|
||||
/// <param name="propertyId">Property ID</param>
|
||||
/// <param name="propertyValue">Property Value</param>
|
||||
public void OnWindowProperties(byte inventoryID, short propertyId, short propertyValue);
|
||||
Task OnWindowPropertiesAsync(byte inventoryID, short propertyId, short propertyValue);
|
||||
|
||||
/// <summary>
|
||||
/// Called when inventory items have been received
|
||||
|
|
@ -283,7 +284,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="inventoryID">Inventory ID</param>
|
||||
/// <param name="itemList">Item list</param>
|
||||
/// <param name="stateId">State ID</param>
|
||||
void OnWindowItems(byte inventoryID, Dictionary<int, Item> itemList, int stateId);
|
||||
Task OnWindowItemsAsync(byte inventoryID, Dictionary<int, Item> itemList, int stateId);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a single slot has been updated inside an inventory
|
||||
|
|
@ -292,14 +293,14 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="slotID">Slot ID</param>
|
||||
/// <param name="item">Item (may be null for empty slot)</param>
|
||||
/// <param name="stateId">State ID</param>
|
||||
void OnSetSlot(byte inventoryID, short slotID, Item? item, int stateId);
|
||||
Task OnSetSlotAsync(byte inventoryID, short slotID, Item? item, int stateId);
|
||||
|
||||
/// <summary>
|
||||
/// Called when player health or hunger changed.
|
||||
/// </summary>
|
||||
/// <param name="health"></param>
|
||||
/// <param name="food"></param>
|
||||
void OnUpdateHealth(float health, int food);
|
||||
Task OnUpdateHealth(float health, int food);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the health of an entity changed
|
||||
|
|
@ -321,7 +322,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="location">Explosion location</param>
|
||||
/// <param name="strength">Explosion strength</param>
|
||||
/// <param name="affectedBlocks">Amount of affected blocks</param>
|
||||
void OnExplosion(Location location, float strength, int affectedBlocks);
|
||||
Task OnExplosion(Location location, float strength, int affectedBlocks);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a player's game mode has changed
|
||||
|
|
@ -335,7 +336,7 @@ namespace MinecraftClient.Protocol
|
|||
/// </summary>
|
||||
/// <param name="uuid">Affected player's UUID</param>
|
||||
/// <param name="latency">latency</param>
|
||||
void OnLatencyUpdate(Guid uuid, int latency);
|
||||
Task OnLatencyUpdate(Guid uuid, int latency);
|
||||
|
||||
/// <summary>
|
||||
/// Called when Experience bar is updated
|
||||
|
|
@ -343,14 +344,14 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="Experiencebar">Experience bar level</param>
|
||||
/// <param name="Level">Player Level</param>
|
||||
/// <param name="TotalExperience">Total experience</param>
|
||||
void OnSetExperience(float Experiencebar, int Level, int TotalExperience);
|
||||
Task OnSetExperience(float Experiencebar, int Level, int TotalExperience);
|
||||
|
||||
/// <summary>
|
||||
/// Called when client need to change slot.
|
||||
/// </summary>
|
||||
/// <remarks>Used for setting player slot after joining game</remarks>
|
||||
/// <param name="slot"></param>
|
||||
void OnHeldItemChange(byte slot);
|
||||
Task OnHeldItemChange(byte slot);
|
||||
|
||||
/// <summary>
|
||||
/// Called when an update of the map is sent by the server, take a look at https://wiki.vg/Protocol#Map_Data for more info on the fields
|
||||
|
|
@ -366,7 +367,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="mapCoulmnX">x offset of the westernmost column</param>
|
||||
/// <param name="mapRowZ">z offset of the northernmost row</param>
|
||||
/// <param name="colors">a byte array of colors on the map</param>
|
||||
void OnMapData(int mapid, byte scale, bool trackingPosition, bool locked, List<MapIcon> icons, byte columnsUpdated, byte rowsUpdated, byte mapCoulmnX, byte mapRowZ, byte[]? colors);
|
||||
Task OnMapData(MapData mapData);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the Player entity ID has been received from the server
|
||||
|
|
@ -384,7 +385,7 @@ namespace MinecraftClient.Protocol
|
|||
/// <param name="flags">effect flags</param>
|
||||
/// <param name="hasFactorData">has factor data</param>
|
||||
/// <param name="factorCodec">factorCodec</param>
|
||||
void OnEntityEffect(int entityid, Effects effect, int amplifier, int duration, byte flags, bool hasFactorData, Dictionary<String, object>? factorCodec);
|
||||
Task OnEntityEffect(int entityid, Effect effect);
|
||||
|
||||
/// <summary>
|
||||
/// Called when coreboardObjective
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace MinecraftClient.Protocol.PacketPipeline
|
|||
private const int BufferSize = 1024;
|
||||
|
||||
public Socket Client;
|
||||
public bool HwAccelerateEnable { init; get; }
|
||||
private bool inStreamEnded = false;
|
||||
|
||||
private readonly IAesHandler Aes;
|
||||
|
|
@ -44,11 +45,20 @@ namespace MinecraftClient.Protocol.PacketPipeline
|
|||
AesBufSend = new byte[BlockSize];
|
||||
|
||||
if (FasterAesX86.IsSupported())
|
||||
{
|
||||
HwAccelerateEnable = true;
|
||||
Aes = new FasterAesX86(key);
|
||||
else if (FasterAesArm.IsSupported())
|
||||
}
|
||||
else if (false && FasterAesArm.IsSupported()) // Further testing required
|
||||
{
|
||||
HwAccelerateEnable = true;
|
||||
Aes = new FasterAesArm(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
HwAccelerateEnable = false;
|
||||
Aes = new BasicAes(key);
|
||||
}
|
||||
|
||||
key.CopyTo(InputBuf.Slice(0, BlockSize));
|
||||
key.CopyTo(OutputBuf.Slice(0, BlockSize));
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace MinecraftClient.Protocol.PacketPipeline
|
|||
public int CompressionThreshold { get; set; } = 0;
|
||||
|
||||
|
||||
private SemaphoreSlim SendSemaphore = new SemaphoreSlim(1, 1);
|
||||
private SemaphoreSlim SendSemaphore = new(1, 1);
|
||||
|
||||
private Task LastSendTask = Task.CompletedTask;
|
||||
|
||||
|
|
@ -78,7 +78,8 @@ namespace MinecraftClient.Protocol.PacketPipeline
|
|||
/// <param name="buffer">data to send</param>
|
||||
public async Task SendAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await SendSemaphore.WaitAsync();
|
||||
await SendSemaphore.WaitAsync(cancellationToken);
|
||||
if (cancellationToken.IsCancellationRequested) return;
|
||||
await LastSendTask;
|
||||
LastSendTask = WriteStream.WriteAsync(buffer, cancellationToken).AsTask();
|
||||
SendSemaphore.Release();
|
||||
|
|
@ -86,7 +87,6 @@ namespace MinecraftClient.Protocol.PacketPipeline
|
|||
|
||||
public async Task<Tuple<int, PacketStream>> GetNextPacket(bool handleCompress, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// ConsoleIO.WriteLine("GetNextPacket");
|
||||
if (packetStream != null)
|
||||
{
|
||||
await packetStream.DisposeAsync();
|
||||
|
|
@ -105,9 +105,18 @@ namespace MinecraftClient.Protocol.PacketPipeline
|
|||
ZlibBaseStream zlibBaseStream = new(AesStream ?? ReadStream, packetSize: packetSize - readed);
|
||||
ZLibStream zlibStream = new(zlibBaseStream, CompressionMode.Decompress, leaveOpen: false);
|
||||
|
||||
zlibBaseStream.BufferSize = 16;
|
||||
(packetID, readed) = await ReceiveVarIntRaw(zlibStream, cancellationToken);
|
||||
zlibBaseStream.BufferSize = 512;
|
||||
if (AesStream == null || AesStream.HwAccelerateEnable)
|
||||
{
|
||||
zlibBaseStream.BufferSize = 64;
|
||||
(packetID, readed) = await ReceiveVarIntRaw(zlibStream, cancellationToken);
|
||||
zlibBaseStream.BufferSize = 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
zlibBaseStream.BufferSize = 16;
|
||||
(packetID, readed) = await ReceiveVarIntRaw(zlibStream, cancellationToken);
|
||||
zlibBaseStream.BufferSize = 256;
|
||||
}
|
||||
|
||||
// ConsoleIO.WriteLine("packetID = " + packetID + ", readed = " + zlibBaseStream.packetReaded + ", size = " + packetSize + " -> " + sizeUncompressed);
|
||||
|
||||
|
|
@ -131,7 +140,7 @@ namespace MinecraftClient.Protocol.PacketPipeline
|
|||
byte[] b = new byte[1];
|
||||
while (true)
|
||||
{
|
||||
await stream.ReadAsync(b);
|
||||
await stream.ReadAsync(b, cancellationToken);
|
||||
i |= (b[0] & 0x7F) << j++ * 7;
|
||||
if (j > 5) throw new OverflowException("VarInt too big");
|
||||
if ((b[0] & 0x80) != 128) break;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace MinecraftClient.Protocol
|
|||
|
||||
// Entity info
|
||||
|
||||
public Mapping.Entity? entity;
|
||||
public EntityHandler.Entity? entity;
|
||||
|
||||
// For message signature
|
||||
|
||||
|
|
|
|||
27
MinecraftClient/Protocol/TitlePacket.cs
Normal file
27
MinecraftClient/Protocol/TitlePacket.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MinecraftClient.Protocol
|
||||
{
|
||||
public record TitlePacket
|
||||
{
|
||||
public int Action { init; get; }
|
||||
|
||||
public string TitleText { init; get; } = string.Empty;
|
||||
|
||||
public string SubtitleText { init; get; } = string.Empty;
|
||||
|
||||
public string ActionbarText { init; get; } = string.Empty;
|
||||
|
||||
public int Stay { init; get; }
|
||||
|
||||
public int FadeIn { init; get; }
|
||||
|
||||
public int FadeOut { init; get; }
|
||||
|
||||
public string JsonText { init; get; } = string.Empty;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue