diff --git a/Minecraft.Client/PendingConnection.cpp b/Minecraft.Client/PendingConnection.cpp index b651c8ad7..10f70392f 100644 --- a/Minecraft.Client/PendingConnection.cpp +++ b/Minecraft.Client/PendingConnection.cpp @@ -241,6 +241,60 @@ void PendingConnection::handleLogin(shared_ptr packet) //if (true)// 4J removed !server->onlineMode) bool sentDisconnect = false; +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + std::string connectionIp = ""; + int connectionPort = 0; + + if (!connection || !connection->getSocket()) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } + + unsigned char smallId = connection->getSocket()->getSmallId(); + if (smallId == 0) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } + + if (!ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &connectionIp)) + { + SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId); + if (sock != INVALID_SOCKET) + { + sockaddr_in addr; + int addrLen = sizeof(addr); + if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) + { + char ipBuf[64] = {}; + if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf))) + { + connectionIp = ipBuf; + connectionPort = (int)ntohs(addr.sin_port); + } + } + } + if (connectionIp.empty()) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } + } + else { + SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId); + if (sock != INVALID_SOCKET) + { + sockaddr_in addr; + int addrLen = sizeof(addr); + if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) + connectionPort = (int)ntohs(addr.sin_port); + } + } + + if (FourKitBridge::FirePlayerLogin(packet->userName, connectionIp, connectionPort, 1, &packet->m_onlineXuid, &packet->m_offlineXuid)) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } +#endif + // Use the same Xuid choice as handleAcceptedLogin (offline first, online fallback). // PlayerUID loginXuid = packet->m_offlineXuid; @@ -381,11 +435,65 @@ void PendingConnection::handleAcceptedLogin(shared_ptr packet) return; } +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + std::string connectionIp = ""; + int connectionPort = 0; + + if (!connection || !connection->getSocket()) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } + + unsigned char smallId = connection->getSocket()->getSmallId(); + if (smallId == 0) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } + + if (!ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &connectionIp)) + { + SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId); + if (sock != INVALID_SOCKET) + { + sockaddr_in addr; + int addrLen = sizeof(addr); + if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) + { + char ipBuf[64] = {}; + if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf))) + { + connectionIp = ipBuf; + connectionPort = (int)ntohs(addr.sin_port); + } + } + } + if (connectionIp.empty()) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } + } + else { + SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId); + if (sock != INVALID_SOCKET) + { + sockaddr_in addr; + int addrLen = sizeof(addr); + if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) + connectionPort = (int)ntohs(addr.sin_port); + } + } + + if (FourKitBridge::FirePlayerLogin(packet->userName, connectionIp, connectionPort, 2, &packet->m_onlineXuid, &packet->m_offlineXuid)) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } +#endif + // Guests use the online xuid, everyone else uses the offline one PlayerUID playerXuid = packet->m_offlineXuid; - if(playerXuid == INVALID_XUID) playerXuid = packet->m_onlineXuid; + if (playerXuid == INVALID_XUID) playerXuid = packet->m_onlineXuid; - shared_ptr playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid,packet->m_onlineXuid); + shared_ptr playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid, packet->m_onlineXuid); if (playerEntity != nullptr) { #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 39ad92ef8..7d9734910 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -118,7 +118,7 @@ void PlayerConnection::tick() #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) if (!hasDoneFirstTickFourKit) { - FourKitBridge::FirePlayerJoin(player->entityId, player->name, player->getUUID()); + FourKitBridge::FirePlayerJoin(player->entityId, player->name, player->getUUID(), (unsigned long long)player->getOnlineXuid(), (unsigned long long)player->getXuid()); hasDoneFirstTickFourKit = true; } #endif diff --git a/Minecraft.Server.FourKit/Entity/Player.cs b/Minecraft.Server.FourKit/Entity/Player.cs index 85adac143..49b93d4bf 100644 --- a/Minecraft.Server.FourKit/Entity/Player.cs +++ b/Minecraft.Server.FourKit/Entity/Player.cs @@ -2,6 +2,7 @@ namespace Minecraft.Server.FourKit.Entity; using System.Runtime.InteropServices; using Minecraft.Server.FourKit.Command; +using Minecraft.Server.FourKit.Experimental; using Minecraft.Server.FourKit.Inventory; using Minecraft.Server.FourKit.Net; @@ -18,12 +19,16 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender private float _exp; private int _totalExperience; private Guid _playerUniqueId; + private ulong _playerRawOnlineXUID; + private ulong _playerRawOfflineXUID; private string? _displayName; private bool _sneaking; private bool _sprinting; private bool _allowFlight; private bool _sleepingIgnored; + private PlayerConnection _connection; + internal bool IsOnline { get; set; } internal Player(int entityId, string name) @@ -80,6 +85,12 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender /// The player's unique identifier. public new Guid getUniqueId() => _playerUniqueId; + + public ulong getRawOnlineXUID() => _playerRawOnlineXUID; + + public ulong getRawOfflineXUID() => _playerRawOfflineXUID; + + /// /// Gets the player's current saturation level. /// Saturation acts as a buffer before hunger begins to deplete. @@ -610,6 +621,8 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender internal void SetSaturationInternal(float saturation) => _saturation = saturation; internal void SetWalkSpeedInternal(float walkSpeed) => _walkSpeed = walkSpeed; internal void SetPlayerUniqueIdInternal(Guid id) => _playerUniqueId = id; + internal void SetPlayerRawOnlineXUIDInternal(ulong xuid) => _playerRawOnlineXUID = xuid; + internal void SetPlayerRawOfflineXUIDInternal(ulong xuid) => _playerRawOfflineXUID = xuid; internal void SetSneakingInternal(bool sneaking) => _sneaking = sneaking; internal void SetSprintingInternal(bool sprinting) => _sprinting = sprinting; internal void SetAllowFlightInternal(bool allowFlight) => _allowFlight = allowFlight; diff --git a/Minecraft.Server.FourKit/Enums/LoginType.cs b/Minecraft.Server.FourKit/Enums/LoginType.cs new file mode 100644 index 000000000..644cd7487 --- /dev/null +++ b/Minecraft.Server.FourKit/Enums/LoginType.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Minecraft.Server.FourKit.Enums; + +public enum LoginType +{ + INITIAL = 1, + ACCEPTED = 2, +} diff --git a/Minecraft.Server.FourKit/Enums/TreeType.cs b/Minecraft.Server.FourKit/Enums/TreeType.cs new file mode 100644 index 000000000..9affb61a6 --- /dev/null +++ b/Minecraft.Server.FourKit/Enums/TreeType.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Minecraft.Server.FourKit.Enums; + +/// +/// Tree and organic structure types. +/// +public enum TreeType { + /// + /// No tree type. + /// + None = 0, + + /// + /// Redwood tree, shaped like a pine tree. + /// + SPRUCE = 1, + + /// + /// Birch tree. + /// + BIRCH = 2, + + /// + /// Standard jungle tree; 4 blocks wide and tall. + /// + JUNGLE = 3, + + /// + /// Regular tree, extra tall with branches. + /// + BIG_OAK = 4, + + /// + /// Regular tree, no branches. + /// + OAK = 5, + + /// + /// Big brown mushroom; tall and umbrella-like. + /// + BROWN_MUSHROOM = 6, + + /// + /// Big red mushroom; short and fat. + /// + RED_MUSHROOM = 7, +} diff --git a/Minecraft.Server.FourKit/Event/Player/PlayerLoginEvent.cs b/Minecraft.Server.FourKit/Event/Player/PlayerLoginEvent.cs new file mode 100644 index 000000000..87fa25f6e --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Player/PlayerLoginEvent.cs @@ -0,0 +1,93 @@ +namespace Minecraft.Server.FourKit.Event.Player; + +using Minecraft.Server.FourKit.Enums; +using Minecraft.Server.FourKit.Net; + +/// +/// Stores details for players attempting to log in. +/// +public class PlayerLoginEvent : Event, Cancellable +{ + private string name; + private InetSocketAddress ipAddress; //bukkit uses InetAddress but we expose port also + + private ulong onlineXuid; + private ulong offlineXuid; + private bool changedXuidValues; + + private LoginType loginType; + + private bool _cancelled; + + internal PlayerLoginEvent(string name, InetSocketAddress ipAddress, LoginType type, ulong onlineXuid, ulong offlineXuid) : base() + { + this.name = name; + this.ipAddress = ipAddress; + this.onlineXuid = onlineXuid; + this.offlineXuid = offlineXuid; + this.changedXuidValues = false; + + this.loginType = type; + } + + public LoginType getLoginType() => loginType; + + + /// + /// Experimental. Gets the online XUID (Xbox User ID), used for guests (splitscreen users). + /// + /// The online XUID value. + public ulong getOnlineXuid() => onlineXuid; + + /// + /// Experimental. Sets the online XUID (Xbox User ID). Marks XUID values as changed. + /// + /// The new online XUID value. + public void setOnlineXuid(ulong newXuid) + { + this.onlineXuid = newXuid; + this.changedXuidValues = true; + } + + /// + /// Experimental. Gets the offline XUID (Xbox User ID), which is the main XUID used by the client. + /// + /// The offline XUID value. + public ulong getOfflineXuid() => offlineXuid; + + /// + /// Experimental. Sets the offline XUID (Xbox User ID). Marks XUID values as changed. + /// + /// The new offline XUID value. + public void setOfflineXuid(ulong newXuid) + { + this.offlineXuid = newXuid; + this.changedXuidValues = true; + } + + /// + /// Experimental. Returns true if either XUID value has been changed via setters. + /// + /// True if XUID values have been changed; otherwise, false. + public bool hasChangedXuidValues() => changedXuidValues; + + + /// + /// Gets the player's name. + /// + /// The player's name. + public string getName() => name; + + + /// + /// Gets the player IP address. + /// + /// The IP address. + public InetSocketAddress getAddress() => ipAddress; + + /// + public bool isCancelled() => _cancelled; + + /// + public void setCancelled(bool cancel) => _cancelled = cancel; +} diff --git a/Minecraft.Server.FourKit/Event/World/StructureGrowEvent.cs b/Minecraft.Server.FourKit/Event/World/StructureGrowEvent.cs new file mode 100644 index 000000000..86567a935 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/World/StructureGrowEvent.cs @@ -0,0 +1,60 @@ +using Minecraft.Server.FourKit.Enums; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Minecraft.Server.FourKit.Event.World; + +/// +/// Event that is called when an organic structure attempts to grow (Sapling -> Tree), (Mushroom -> Huge Mushroom), naturally or using bonemeal. +/// +public class StructureGrowEvent : WorldEvent, Cancellable +{ + internal Location _location; + internal TreeType _treeType; + internal bool _wasBonemeal; + internal Minecraft.Server.FourKit.Entity.Player? _player; + + internal bool _cancelled; + internal StructureGrowEvent(Location location, TreeType treeType, bool wasBonemeal, Minecraft.Server.FourKit.Entity.Player? player) : base(location.getWorld()) + { + _location = location; + _treeType = treeType; + _wasBonemeal = wasBonemeal; + _player = player; + } + + + /// + /// Gets the species type (birch, normal, pine, red mushroom, brown mushroom). + /// + /// Structure species + public TreeType getSpecies() => _treeType; + + + /// + /// Gets the location of the structure. + /// + /// Location of the structure + public Location getLocation() => _location; + + + /// + /// Checks if structure was grown using bonemeal. + /// + /// True if the structure was grown using bonemeal. + public bool isFromBonemeal() => _wasBonemeal; + + + /// + /// Gets the player that created the structure. + /// + /// Player that created the structure, null if was not created manually + public Minecraft.Server.FourKit.Entity.Player? getPlayer() => _player; + + /// + public bool isCancelled() => _cancelled; + + /// + public void setCancelled(bool cancel) => _cancelled = cancel; +} diff --git a/Minecraft.Server.FourKit/Event/World/WorldEvent.cs b/Minecraft.Server.FourKit/Event/World/WorldEvent.cs new file mode 100644 index 000000000..4d30e8835 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/World/WorldEvent.cs @@ -0,0 +1,13 @@ +namespace Minecraft.Server.FourKit.Event.World; + +using Minecraft.Server.FourKit; + +public class WorldEvent : Event +{ + internal World? _world; + + public WorldEvent(World? world) : base() + { + _world = world; + } +} diff --git a/Minecraft.Server.FourKit/Experimental/PlayerConnection.cs b/Minecraft.Server.FourKit/Experimental/PlayerConnection.cs new file mode 100644 index 000000000..5a56ed453 --- /dev/null +++ b/Minecraft.Server.FourKit/Experimental/PlayerConnection.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Minecraft.Server.FourKit.Experimental; +public class PlayerConnection +{ + +} diff --git a/Minecraft.Server.FourKit/FourKitHost.Events.cs b/Minecraft.Server.FourKit/FourKitHost.Events.cs index 85ecc193b..3d2f3c6cb 100644 --- a/Minecraft.Server.FourKit/FourKitHost.Events.cs +++ b/Minecraft.Server.FourKit/FourKitHost.Events.cs @@ -8,6 +8,8 @@ using Minecraft.Server.FourKit.Event.Player; using Minecraft.Server.FourKit.Event.Inventory; using Minecraft.Server.FourKit.Inventory; using Minecraft.Server.FourKit.Net; +using Minecraft.Server.FourKit.Event.World; +using Minecraft.Server.FourKit.Enums; namespace Minecraft.Server.FourKit; @@ -38,7 +40,38 @@ public static partial class FourKitHost } [UnmanagedCallersOnly] - public static void FirePlayerJoin(int entityId, IntPtr namePtr, int nameByteLen, IntPtr uuidPtr, int uuidByteLen) + public static int FirePlayerLogin(IntPtr namePtr, int nameByteLen, IntPtr ipPtr, int ipByteLen, int port, int type, IntPtr offlineXUIDPtr, IntPtr onlineXUIDPtr) + { + try + { + string name = nameByteLen > 0 + ? Marshal.PtrToStringUTF8(namePtr, nameByteLen) ?? string.Empty + : string.Empty; + + string ipStr = ipByteLen > 0 + ? Marshal.PtrToStringUTF8(ipPtr, ipByteLen) ?? string.Empty + : string.Empty; + + var evt = new PlayerLoginEvent(name, new InetSocketAddress(new InetAddress(ipStr), port), (LoginType)type, unchecked((ulong)Marshal.ReadInt64(onlineXUIDPtr)), unchecked((ulong)Marshal.ReadInt64(offlineXUIDPtr))); + FourKit.FireEvent(evt); + + if (evt.hasChangedXuidValues()) + { + Marshal.WriteInt64(offlineXUIDPtr, unchecked((long)evt.getOfflineXuid())); + Marshal.WriteInt64(onlineXUIDPtr, unchecked((long)evt.getOnlineXuid())); + } + + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FirePlayerLogin error: {ex}"); + return 0; + } + } + + [UnmanagedCallersOnly] + public static void FirePlayerJoin(int entityId, IntPtr namePtr, int nameByteLen, IntPtr uuidPtr, int uuidByteLen, ulong offlineXUIDPtr, ulong onlineXUIDPtr) { try { @@ -52,6 +85,8 @@ public static partial class FourKitHost var player = FourKit.TrackPlayer(entityId, name); player.SetPlayerUniqueIdInternal(ParseOrHashGuid(uuidStr)); + player.SetPlayerRawOnlineXUIDInternal(onlineXUIDPtr); + player.SetPlayerRawOfflineXUIDInternal(offlineXUIDPtr); SyncPlayerFromNative(player); var evt = new PlayerJoinEvent(player); FourKit.FireEvent(evt); @@ -189,6 +224,25 @@ public static partial class FourKitHost } } + [UnmanagedCallersOnly] + public static int FireStructureGrow(int dimId, int x, int y, int z, int treeType, int wasBonemeal, int entityId) + { + try + { + Location location = new Location(FourKit.getWorld(dimId), x, y, z); + + StructureGrowEvent evt = new StructureGrowEvent(location, (TreeType)treeType, wasBonemeal == 1, (wasBonemeal == 1 && entityId != -1 ? FourKit.GetPlayerByEntityId(entityId) : null)); + FourKit.FireEvent(evt); + + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FireStructureGrow error: {ex}"); + return 0; + } + } + [UnmanagedCallersOnly] public static int FirePlayerChat(int entityId, IntPtr msgPtr, int msgByteLen, IntPtr outBuf, int outBufSize, IntPtr outLenPtr) diff --git a/Minecraft.Server.FourKit/World.cs b/Minecraft.Server.FourKit/World.cs index f063229cb..1d85fe9cc 100644 --- a/Minecraft.Server.FourKit/World.cs +++ b/Minecraft.Server.FourKit/World.cs @@ -18,7 +18,11 @@ public class World _name = name; } - internal int getDimensionId() => _dimensionId; + /// + /// Gets the dimension ID of this world. + /// + /// Dimension ID of this world. + public int getDimensionId() => _dimensionId; /// /// Gets the unique name of this world. diff --git a/Minecraft.Server.FourKit/docs/usage-of-all-events.md b/Minecraft.Server.FourKit/docs/usage-of-all-events.md index a3f7394b6..7103fd7f6 100644 --- a/Minecraft.Server.FourKit/docs/usage-of-all-events.md +++ b/Minecraft.Server.FourKit/docs/usage-of-all-events.md @@ -10,6 +10,40 @@ Events that implement \ref Minecraft.Server.FourKit.Event.Cancellable "Cancellab @section player_events Player Events +@subsection playerloginevent PlayerLoginEvent + +\ref Minecraft.Server.FourKit.Event.Player.PlayerLoginEvent "PlayerLoginEvent" is fired when a player is about to log in, after pre-login checks but before the join event. You can inspect or modify the player's XUIDs, name, and IP address, and cancel the login. The XUID API is **experimental**: you can set and get the raw XUID values. The offline XUID is the main XUID used by the client; the online XUID is used for guests (splitscreen users). + +```csharp +[EventHandler] +public void onLogin(PlayerLoginEvent e) +{ + // block a specific XUID + if (e.getOfflineXuid() == 12345678901234567UL) + { + e.setCancelled(true); + } + + // change the XUID (experimental) + e.setOfflineXuid(98765432109876543UL); +} +``` + +| Method | Description | +|--------|-------------| +| `getName()` | The player's username attempting to log in. | +| `getAddress()` | The \ref Minecraft.Server.FourKit.Net.InetSocketAddress "InetSocketAddress" of the connection. | +| `getLoginType()` | The login type (e.g. online, offline). | +| `getOfflineXuid()` | **Experimental.** The main XUID used by the client. | +| `setOfflineXuid(ulong)` | **Experimental.** Set the offline XUID. | +| `getOnlineXuid()` | **Experimental.** The XUID used for guests (splitscreen users). | +| `setOnlineXuid(ulong)` | **Experimental.** Set the online XUID. | +| `hasChangedXuidValues()` | **Experimental.** True if XUID values were changed. | +| `isCancelled()` | Whether the login is cancelled. | +| `setCancelled(bool)` | Cancel or allow the login. | + +> **Cancellable:** Yes + @subsection playerpreloginevent PlayerPreLoginEvent \ref Minecraft.Server.FourKit.Event.Player.PlayerPreLoginEvent "PlayerPreLoginEvent" is fired before a player is allowed to join the server. You can inspect the players name and IP address, and cancel the event to prevent them from joining (such as for bans, whitelists, etc). @@ -556,6 +590,44 @@ public void onPlayerDeath(PlayerDeathEvent e) --- + +@section world_events World Events + +@subsection structuregrowevent StructureGrowEvent + +\ref Minecraft.Server.FourKit.Event.World.StructureGrowEvent "StructureGrowEvent" is fired when an organic structure attempts to grow (Sapling -> Tree), (Mushroom -> Huge Mushroom), either naturally or using bonemeal. You can cancel it, inspect the location, species, and optionally the player who caused it. + +```csharp +[EventHandler] +public void onStructureGrow(StructureGrowEvent e) +{ + // prevent huge mushrooms from growing + if (e.getSpecies() == TreeType.BROWN_MUSHROOM || e.getSpecies() == TreeType.RED_MUSHROOM) + { + e.setCancelled(true); + } + + // send a message if grown by player + var player = e.getPlayer(); + if (player != null) + { + player.sendMessage($"You grew a {e.getSpecies()} at {e.getLocation()}"); + } +} +``` + +| Method | Description | +|--------|-------------| +| `getLocation()` | The location of the structure. | +| `getSpecies()` | The species type (birch, normal, pine, red mushroom, brown mushroom). | +| `isFromBonemeal()` | True if the structure was grown using bonemeal. | +| `getPlayer()` | The player that created the structure, null if not created manually. | +| `isCancelled()` | Whether the event is cancelled. | +| `setCancelled(bool)` | Cancel or allow the structure growth. | + +> **Cancellable:** Yes + +--- @section block_events Block Events @subsection blockbreakevent BlockBreakEvent diff --git a/Minecraft.Server/FourKitBridge.cpp b/Minecraft.Server/FourKitBridge.cpp index 7e9094516..da7b16822 100644 --- a/Minecraft.Server/FourKitBridge.cpp +++ b/Minecraft.Server/FourKitBridge.cpp @@ -17,8 +17,9 @@ namespace FourKitBridge { typedef void(__stdcall *fn_initialize)(); -typedef int(__stdcall *fn_fire_player_prelogin)(const char* nameUtf8, int nameByteLen, const char* ipUtf8, int ipByteLen, int port); -typedef void(__stdcall *fn_fire_player_join)(int entityId, const char *nameUtf8, int nameByteLen, const char *uuidUtf8, int uuidByteLen); +typedef int(__stdcall* fn_fire_player_prelogin)(const char* nameUtf8, int nameByteLen, const char* ipUtf8, int ipByteLen, int port); +typedef int(__stdcall *fn_fire_player_login)(const char* nameUtf8, int nameByteLen, const char* ipUtf8, int ipByteLen, int port, int type, unsigned long long* offlineXUID, unsigned long long* onlineXUID); +typedef void(__stdcall *fn_fire_player_join)(int entityId, const char *nameUtf8, int nameByteLen, const char *uuidUtf8, int uuidByteLen, unsigned long long offlineXUID, unsigned long long onlineXUID); typedef void(__stdcall *fn_fire_player_quit)(int entityId); typedef int(__stdcall *fn_fire_player_kick)(int entityId, int disconnectReason, const char *reasonUtf8, int reasonByteLen, @@ -31,6 +32,7 @@ typedef int(__stdcall *fn_fire_player_move)(int entityId, typedef void(__stdcall *fn_set_native_callbacks)(void *damage, void *setHealth, void *teleport, void *setGameMode, void *broadcastMessage, void *setFallDistance, void *getPlayerSnapshot, void *sendMessage, void *setWalkSpeed, void *teleportEntity); typedef void(__stdcall *fn_set_world_callbacks)(void *getTileId, void *getTileData, void *setTile, void *setTileData, void *breakBlock, void *getHighestBlockY, void *getWorldInfo, void *setWorldTime, void *setWeather, void *createExplosion, void *strikeLightning, void *setSpawnLocation, void *dropItem); typedef void(__stdcall *fn_update_entity_id)(int oldEntityId, int newEntityId); +typedef int(__stdcall *fn_fire_structure_grow)(int dimId, int x, int y, int z, int species, int wasBonemeal, int entityId); typedef int(__stdcall *fn_fire_player_chat)(int entityId, const char *msgUtf8, int msgByteLen, char *outBuf, int outBufSize, int *outLen); typedef int(__stdcall *fn_fire_block_place)(int entityId, int dimId, int placedX, int placedY, int placedZ, @@ -102,6 +104,7 @@ static std::unordered_map s_openContainerInfo; static fn_initialize s_managedInit = nullptr; static fn_fire_player_prelogin s_managedFirePreLogin = nullptr; +static fn_fire_player_login s_managedFireLogin = nullptr; static fn_fire_player_join s_managedFireJoin = nullptr; static fn_update_entity_id s_managedUpdateEntityId = nullptr; static fn_fire_player_quit s_managedFireQuit = nullptr; @@ -110,6 +113,7 @@ static fn_shutdown s_managedShutdown = nullptr; static fn_fire_player_move s_managedFireMove = nullptr; static fn_set_native_callbacks s_managedSetCallbacks = nullptr; static fn_set_world_callbacks s_managedSetWorldCallbacks = nullptr; +static fn_fire_structure_grow s_managedFireStructureGrow = nullptr; static fn_fire_player_chat s_managedFireChat = nullptr; static fn_fire_block_place s_managedFireBlockPlace = nullptr; static fn_fire_block_break s_managedFireBlockBreak = nullptr; @@ -167,7 +171,8 @@ void Initialize() struct { const wchar_t *name; void **target; } entries[] = { {L"Initialize", (void **)&s_managedInit}, - {L"FirePlayerPreLogin", (void **)&s_managedFirePreLogin}, + {L"FirePlayerPreLogin", (void**)&s_managedFirePreLogin}, + {L"FirePlayerLogin", (void **)&s_managedFireLogin}, {L"FirePlayerJoin", (void **)&s_managedFireJoin}, {L"FirePlayerQuit", (void **)&s_managedFireQuit}, {L"FirePlayerKick", (void **)&s_managedFireKick}, @@ -176,7 +181,8 @@ void Initialize() {L"SetNativeCallbacks", (void **)&s_managedSetCallbacks}, {L"SetWorldCallbacks", (void **)&s_managedSetWorldCallbacks}, {L"UpdatePlayerEntityId", (void **)&s_managedUpdateEntityId}, - {L"FirePlayerChat", (void **)&s_managedFireChat}, + {L"FirePlayerChat", (void**)&s_managedFireChat}, + {L"FireStructureGrow", (void **)&s_managedFireStructureGrow}, {L"FireBlockPlace", (void **)&s_managedFireBlockPlace}, {L"FireBlockBreak", (void **)&s_managedFireBlockBreak}, {L"FireEntityDamage", (void **)&s_managedFireEntityDamage}, @@ -308,7 +314,8 @@ void Shutdown() LogInfo("fourkit", "FourKit shut down."); } -bool FirePlayerPreLogin(const std::wstring& name, const std::string& ip, int port) { +bool FirePlayerPreLogin(const std::wstring& name, const std::string& ip, int port) +{ if (!s_initialized || !s_managedFirePreLogin) { return true; @@ -325,7 +332,26 @@ bool FirePlayerPreLogin(const std::wstring& name, const std::string& ip, int por return canceled != 0; } -void FirePlayerJoin(int entityId, const std::wstring &name, const std::wstring &uuid) +bool FirePlayerLogin(const std::wstring& name, const std::string& ip, int port, int type, unsigned long long* onlineXUID, unsigned long long* offlineXUID) +{ + if (!s_initialized || !s_managedFireLogin) + { + return true; + } + + std::string nameUtf8 = ServerRuntime::StringUtils::WideToUtf8(name); + + int canceled = s_managedFireLogin( + nameUtf8.empty() ? "" : nameUtf8.data(), (int)nameUtf8.size(), + ip.empty() ? "" : ip.data(), (int)ip.size(), + port, type, offlineXUID, onlineXUID); + + LogDebugf("fourkit", "Fired PlayerLogin: %s", nameUtf8.data()); + + return canceled != 0; +} + +void FirePlayerJoin(int entityId, const std::wstring &name, const std::wstring &uuid, unsigned long long onlineXUID, unsigned long long offlineXUID) { if (!s_initialized || !s_managedFireJoin) { @@ -336,7 +362,8 @@ void FirePlayerJoin(int entityId, const std::wstring &name, const std::wstring & std::string uuidUtf8 = ServerRuntime::StringUtils::WideToUtf8(uuid); s_managedFireJoin(entityId, nameUtf8.empty() ? "" : nameUtf8.data(), (int)nameUtf8.size(), - uuidUtf8.empty() ? "" : uuidUtf8.data(), (int)uuidUtf8.size()); + uuidUtf8.empty() ? "" : uuidUtf8.data(), (int)uuidUtf8.size(), + offlineXUID, onlineXUID); LogDebugf("fourkit", "Fired PlayerJoin: entityId=%d", entityId); } @@ -421,6 +448,23 @@ bool FirePlayerMove(int entityId, return cancelled != 0; } +bool FireStructureGrow(int dimId, int x, int y, int z, int treeType, bool wasBonemeal, int entityId) +{ + if (!s_initialized || !s_managedFireStructureGrow) + { + return false; + } + + int cancelled = s_managedFireStructureGrow(dimId, x, y, z, treeType, wasBonemeal ? 1 : 0, entityId); + + if (cancelled != 0) + { + return true; + } + + return false; +} + bool FirePlayerChat(int entityId, const std::wstring &message, std::wstring &outFormatted) diff --git a/Minecraft.Server/FourKitBridge.h b/Minecraft.Server/FourKitBridge.h index 780130090..2f07211a1 100644 --- a/Minecraft.Server/FourKitBridge.h +++ b/Minecraft.Server/FourKitBridge.h @@ -6,7 +6,8 @@ namespace FourKitBridge void Initialize(); void Shutdown(); bool FirePlayerPreLogin(const std::wstring& name, const std::string& ip, int port); - void FirePlayerJoin(int entityId, const std::wstring &name, const std::wstring &uuid); + bool FirePlayerLogin(const std::wstring& name, const std::string& ip, int port, int type, unsigned long long* onlineXUID, unsigned long long* offlineXUID); + void FirePlayerJoin(int entityId, const std::wstring& name, const std::wstring& uuid, unsigned long long onlineXUID, unsigned long long offlineXUID); bool FirePlayerQuit(int entityId); bool FirePlayerKick(int entityId, int disconnectReason, std::wstring &outLeaveMessage); @@ -14,6 +15,7 @@ namespace FourKitBridge double fromX, double fromY, double fromZ, double toX, double toY, double toZ, double *outToX, double *outToY, double *outToZ); + bool FireStructureGrow(int dimId, int x, int y, int z, int treeType, bool wasBonemeal = false, int entityId = -1); bool FirePlayerChat(int entityId, const std::wstring &message, std::wstring &outFormatted); diff --git a/Minecraft.Server/cmake/sources/Common.cmake b/Minecraft.Server/cmake/sources/Common.cmake index b6c4577a0..9805d573a 100644 --- a/Minecraft.Server/cmake/sources/Common.cmake +++ b/Minecraft.Server/cmake/sources/Common.cmake @@ -504,6 +504,12 @@ set(_MINECRAFT_SERVER_COMMON_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/Player.h" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/ThrownEnderPearl.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/Tile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/DyePowderItem.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/DyePowderItem.h" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/Mushroom.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/Mushroom.h" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/Sapling.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/Sapling.h" "${CMAKE_CURRENT_SOURCE_DIR}/../include/lce_filesystem/lce_filesystem.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Console/ServerCliInput.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Console/ServerCliInput.h" diff --git a/Minecraft.World/DyePowderItem.cpp b/Minecraft.World/DyePowderItem.cpp index 3392da982..7fd9ff010 100644 --- a/Minecraft.World/DyePowderItem.cpp +++ b/Minecraft.World/DyePowderItem.cpp @@ -127,8 +127,11 @@ bool DyePowderItem::useOn(shared_ptr itemInstance, shared_ptrgetAuxValue() == WHITE) { // bone meal is a fertilizer, so instantly grow trees and stuff - +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (growCrop(itemInstance, level, x, y, z, bTestUseOnOnly, player->entityId)) +#else if (growCrop(itemInstance, level, x, y, z, bTestUseOnOnly)) +#endif { if (!level->isClientSide) level->levelEvent(LevelEvent::PARTICLES_PLANT_GROWTH, x, y, z, 0); return true; @@ -167,8 +170,7 @@ bool DyePowderItem::useOn(shared_ptr itemInstance, shared_ptr itemInstance, Level *level, int x, int y, int z, bool bTestUseOnOnly) +bool DyePowderItem::growCrop(shared_ptr itemInstance, Level *level, int x, int y, int z, bool bTestUseOnOnly, int entityId) { int tile = level->getTile(x, y, z); if (tile == Tile::sapling_Id) @@ -177,7 +179,11 @@ bool DyePowderItem::growCrop(shared_ptr itemInstance, Level *level { if (!level->isClientSide) { - if (level->random->nextFloat() < 0.45) static_cast(Tile::sapling)->advanceTree(level, x, y, z, level->random); +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (level->random->nextFloat() < 0.45) static_cast(Tile::sapling)->advanceTree(level, x, y, z, level->random, false, entityId); +#else + if (level->random->nextFloat() < 0.45) static_cast(Tile::sapling)->advanceTree(level, x, y, z, level->random); +#endif itemInstance->count--; } } @@ -189,7 +195,11 @@ bool DyePowderItem::growCrop(shared_ptr itemInstance, Level *level { if (!level->isClientSide) { - if (level->random->nextFloat() < 0.4) static_cast(Tile::tiles[tile])->growTree(level, x, y, z, level->random); +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (level->random->nextFloat() < 0.4) static_cast(Tile::tiles[tile])->growTree(level, x, y, z, level->random, false, entityId); +#else + if (level->random->nextFloat() < 0.4) static_cast(Tile::tiles[tile])->growTree(level, x, y, z, level->random); +#endif itemInstance->count--; } } diff --git a/Minecraft.World/DyePowderItem.h b/Minecraft.World/DyePowderItem.h index 270b54246..5f41479b6 100644 --- a/Minecraft.World/DyePowderItem.h +++ b/Minecraft.World/DyePowderItem.h @@ -43,7 +43,7 @@ public: virtual unsigned int getDescriptionId(shared_ptr itemInstance); virtual unsigned int getUseDescriptionId(shared_ptr itemInstance); virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); - static bool growCrop(shared_ptr itemInstance, Level *level, int x, int y, int z, bool bTestUseOnOnly); + static bool growCrop(shared_ptr itemInstance, Level* level, int x, int y, int z, bool bTestUseOnOnly, int entityid = -1); static void addGrowthParticles(Level *level, int x, int y, int z, int count); virtual bool interactEnemy(shared_ptr itemInstance, shared_ptr player, shared_ptr mob); diff --git a/Minecraft.World/Mushroom.cpp b/Minecraft.World/Mushroom.cpp index ca8a85546..1ad2a2b38 100644 --- a/Minecraft.World/Mushroom.cpp +++ b/Minecraft.World/Mushroom.cpp @@ -2,8 +2,13 @@ #include "net.minecraft.world.level.h" #include "net.minecraft.world.level.levelgen.feature.h" #include "net.minecraft.world.h" +#include "Dimension.h" #include "Mushroom.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../Minecraft.Server/FourKitBridge.h" +#endif + Mushroom::Mushroom(int id) : Bush(id) { this->updateDefaultShape(); @@ -71,8 +76,7 @@ bool Mushroom::canSurvive(Level *level, int x, int y, int z) return below == Tile::mycel_Id || (level->getDaytimeRawBrightness(x, y, z) < 13 && mayPlaceOn(below)); } - -bool Mushroom::growTree(Level *level, int x, int y, int z, Random *random) +bool Mushroom::growTree(Level *level, int x, int y, int z, Random *random, bool naturalGrowth, int entityId) { int data = level->getData(x, y, z); @@ -88,6 +92,13 @@ bool Mushroom::growTree(Level *level, int x, int y, int z, Random *random) f = new HugeMushroomFeature(1); } +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireStructureGrow(level->dimension->id, x, y, z, (id == Tile::mushroom_brown_Id ? 6 : 7), !naturalGrowth, entityId)) { + if (f != nullptr) delete f; + f = nullptr; + } +#endif + if (f == nullptr || !f->place(level, random, x, y, z)) { level->setTileAndData(x, y, z, id, data, Tile::UPDATE_ALL); diff --git a/Minecraft.World/Mushroom.h b/Minecraft.World/Mushroom.h index 65a3ea1d9..286cf4d39 100644 --- a/Minecraft.World/Mushroom.h +++ b/Minecraft.World/Mushroom.h @@ -16,5 +16,5 @@ protected: virtual bool mayPlaceOn(int tile); public: virtual bool canSurvive(Level *level, int x, int y, int z); - bool growTree(Level *level, int x, int y, int z, Random *random); + bool growTree(Level* level, int x, int y, int z, Random* random, bool naturalGrowth = true, int entityId = -1); }; diff --git a/Minecraft.World/Sapling.cpp b/Minecraft.World/Sapling.cpp index c169a6082..a2bf6d7e9 100644 --- a/Minecraft.World/Sapling.cpp +++ b/Minecraft.World/Sapling.cpp @@ -3,9 +3,14 @@ #include "net.minecraft.world.level.tile.h" #include "net.minecraft.world.level.levelgen.feature.h" #include "net.minecraft.world.h" - +#include "Dimension.h" #include "Sapling.h" +#if defined(_WINDOWS64) +#include "../Minecraft.Server/FourKitBridge.h" +#endif + + int Sapling::SAPLING_NAMES[SAPLING_NAMES_SIZE] = { IDS_TILE_SAPLING_OAK, IDS_TILE_SAPLING_SPRUCE, IDS_TILE_SAPLING_BIRCH, @@ -47,8 +52,7 @@ Icon *Sapling::getTexture(int face, int data) data = data & TYPE_MASK; return icons[data]; } - -void Sapling::advanceTree(Level *level, int x, int y, int z, Random *random) +void Sapling::advanceTree(Level* level, int x, int y, int z, Random* random, bool naturalGrowth, int entityId) { int data = level->getData(x, y, z); if ((data & AGE_BIT) == 0) @@ -57,11 +61,10 @@ void Sapling::advanceTree(Level *level, int x, int y, int z, Random *random) } else { - growTree(level, x, y, z, random); + growTree(level, x, y, z, random, naturalGrowth, entityId); } } - -void Sapling::growTree(Level *level, int x, int y, int z, Random *random) +void Sapling::growTree(Level* level, int x, int y, int z, Random* random, bool naturalGrowth, int entityId) { int data = level->getData(x, y, z) & TYPE_MASK; @@ -124,7 +127,25 @@ void Sapling::growTree(Level *level, int x, int y, int z, Random *random) { level->setTileAndData(x, y, z, 0, 0, Tile::UPDATE_NONE); } - if (!f->place(level, random, x + ox, y, z + oz)) + +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + int TreeType = data; + if (data != TYPE_EVERGREEN && data != TYPE_BIRCH && data != TYPE_JUNGLE) { + if (dynamic_cast(f) != nullptr) { + TreeType = 4; //large tree + } + else { + TreeType = 5; //normal tree + } + } + + if (FourKitBridge::FireStructureGrow(level->dimension->id, x, y, z, TreeType, !naturalGrowth, entityId)) { + if (f != nullptr) delete f; + f = nullptr; + } +#endif + + if (f == nullptr || !f->place(level, random, x + ox, y, z + oz)) { if (multiblock) { diff --git a/Minecraft.World/Sapling.h b/Minecraft.World/Sapling.h index 57135f330..d48fba206 100644 --- a/Minecraft.World/Sapling.h +++ b/Minecraft.World/Sapling.h @@ -36,8 +36,8 @@ public: virtual void tick(Level *level, int x, int y, int z, Random *random); virtual Icon *getTexture(int face, int data); - virtual void advanceTree(Level *level, int x, int y, int z, Random *random); - void growTree(Level *level, int x, int y, int z, Random *random); + virtual void advanceTree(Level* level, int x, int y, int z, Random* random, bool naturalGrowth = true, int entityId = -1); + void growTree(Level* level, int x, int y, int z, Random* random, bool naturalGrowth = true, int entityId = -1); virtual unsigned int getDescriptionId(int iData = -1); bool isSapling(Level *level, int x, int y, int z, int type);