diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 92ada058b..1c82c1021 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -938,7 +938,10 @@ void PlayerConnection::handleChat(shared_ptr packet) void PlayerConnection::handleCommand(const wstring& message) { #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) - if (FourKitBridge::HandlePlayerCommand(player->entityId, message)) + std::wstring commandLine = message; + if (FourKitBridge::FireCommandPreprocess(player->entityId, commandLine, commandLine)) + return; + if (FourKitBridge::HandlePlayerCommand(player->entityId, commandLine)) return; #endif // 4J - TODO diff --git a/Minecraft.Client/ServerLevel.cpp b/Minecraft.Client/ServerLevel.cpp index 358e4f3ae..a4b75cfff 100644 --- a/Minecraft.Client/ServerLevel.cpp +++ b/Minecraft.Client/ServerLevel.cpp @@ -39,6 +39,9 @@ #include "..\Minecraft.World\ProgressListener.h" #include "PS3\PS3Extras\ShutdownManager.h" #include "PlayerChunkMap.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#endif WeighedTreasureArray ServerLevel::RANDOM_BONUS_ITEMS; @@ -511,15 +514,21 @@ void ServerLevel::tickTiles() int val = (randValue >> 2); int x = (val & 15); int z = ((val >> 8) & 15); - int yy = getTopRainBlock(x + xo, z + zo); - if (shouldFreeze(x + xo, yy - 1, z + zo)) - { - setTileAndUpdate(x + xo, yy - 1, z + zo, Tile::ice_Id); - } - if (isRaining() && shouldSnow(x + xo, yy, z + zo)) - { - setTileAndUpdate(x + xo, yy, z + zo, Tile::topSnow_Id); - } + int yy = getTopRainBlock(x + xo, z + zo); + if (shouldFreeze(x + xo, yy - 1, z + zo)) + { + #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (!FourKitBridge::FireBlockForm(dimension->id, x + xo, yy - 1, z + zo, Tile::ice_Id, 0)) + #endif + setTileAndUpdate(x + xo, yy - 1, z + zo, Tile::ice_Id); + } + if (isRaining() && shouldSnow(x + xo, yy, z + zo)) + { + #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (!FourKitBridge::FireBlockForm(dimension->id, x + xo, yy, z + zo, Tile::topSnow_Id, 0)) + #endif + setTileAndUpdate(x + xo, yy, z + zo, Tile::topSnow_Id); + } if (isRaining()) { Biome *b = getBiome(x + xo, z + zo); diff --git a/Minecraft.Server.FourKit/Block/BlockState.cs b/Minecraft.Server.FourKit/Block/BlockState.cs new file mode 100644 index 000000000..343abbb15 --- /dev/null +++ b/Minecraft.Server.FourKit/Block/BlockState.cs @@ -0,0 +1,197 @@ +namespace Minecraft.Server.FourKit.Block; + +/// +/// Represents a captured state of a block, which will not change +/// automatically. +/// +/// Unlike , which only one object can exist per +/// coordinate, BlockState can exist multiple times for any given Block. +/// Note that another plugin may change the state of the block and you will +/// not know, or they may change the block to another type entirely, causing +/// your BlockState to become invalid. +/// +public class BlockState +{ + private readonly World _world; + private readonly int _x; + private readonly int _y; + private readonly int _z; + private int _typeId; + private int _data; + + internal BlockState(World world, int x, int y, int z, int typeId, int data) + { + _world = world; + _x = x; + _y = y; + _z = z; + _typeId = typeId; + _data = data; + } + + /// + /// Gets the block represented by this BlockState. + /// + /// Block that this BlockState represents. + public Block getBlock() + { + return new Block(_world, _x, _y, _z); + } + + /// + /// Gets the metadata for this block. + /// + /// Block specific metadata. + public int getData() => _data; + + /// + /// Sets the metadata for this block. + /// + /// New block specific metadata. + public void setData(int data) + { + _data = data; + } + + /// + /// Gets the type of this block. + /// + /// Block type. + public Material getType() + { + return Enum.IsDefined(typeof(Material), _typeId) ? (Material)_typeId : Material.AIR; + } + + /// + /// Gets the type ID of this block. + /// + /// Block type ID. + public int getTypeId() => _typeId; + + /// + /// Gets the world which contains this Block. + /// + /// World containing this block. + public World getWorld() => _world; + + /// + /// Gets the x-coordinate of this block. + /// + /// X-coordinate. + public int getX() => _x; + + /// + /// Gets the y-coordinate of this block. + /// + /// Y-coordinate. + public int getY() => _y; + + /// + /// Gets the z-coordinate of this block. + /// + /// Z-coordinate. + public int getZ() => _z; + + /// + /// Gets the location of this block. + /// + /// Location. + public Location getLocation() + { + return new Location(_world, _x, _y, _z, 0f, 0f); + } + + /// + /// Stores the location of this block in the provided Location object. + /// If the provided Location is null this method does nothing and returns + /// null. + /// + /// The location object to store in. + /// The Location object provided or null. + public Location? getLocation(Location? loc) + { + if (loc == null) return null; + loc.X = _x; + loc.Y = _y; + loc.Z = _z; + loc.LocationWorld = _world; + return loc; + } + + /// + /// Sets the type of this block. + /// + /// Material to change this block to. + public void setType(Material type) + { + _typeId = (int)type; + } + + /// + /// Sets the type ID of this block. + /// + /// Type ID to change this block to. + /// Whether the change was accepted. + public bool setTypeId(int type) + { + _typeId = type; + return true; + } + + /// + /// Attempts to update the block represented by this state, setting it to + /// the new values as defined by this state. + /// This has the same effect as calling update(false). + /// + /// true if the update was successful, otherwise + /// false. + public bool update() + { + return update(false); + } + + /// + /// Attempts to update the block represented by this state, setting it to + /// the new values as defined by this state. + /// This has the same effect as calling + /// update(force, true). + /// + /// true to forcefully set the state. + /// true if the update was successful, otherwise + /// false. + public bool update(bool force) + { + return update(force, true); + } + + /// + /// Attempts to update the block represented by this state, setting it to + /// the new values as defined by this state. + /// Unless is true, this will not modify the + /// state of a block if it is no longer the same type as it was when this + /// state was taken. It will return false in this eventuality. + /// If is true, it will set the type of the + /// block to match the new state, set the state data and then return + /// true. + /// If is true, it will trigger a + /// physics update on surrounding blocks which could cause them to update + /// or disappear. + /// + /// true to forcefully set the state. + /// false to cancel updating physics on + /// surrounding blocks. + /// true if the update was successful, otherwise + /// false. + public bool update(bool force, bool applyPhysics) + { + if (NativeBridge.GetTileId == null || NativeBridge.SetTile == null) + return false; + + int currentType = NativeBridge.GetTileId(_world.getDimensionId(), _x, _y, _z); + if (!force && currentType != _typeId) + return false; + + NativeBridge.SetTile(_world.getDimensionId(), _x, _y, _z, _typeId, _data); + return true; + } +} diff --git a/Minecraft.Server.FourKit/Event/Block/BlockBreakEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockBreakEvent.cs index 251391433..410fc5bff 100644 --- a/Minecraft.Server.FourKit/Event/Block/BlockBreakEvent.cs +++ b/Minecraft.Server.FourKit/Event/Block/BlockBreakEvent.cs @@ -39,17 +39,10 @@ public class BlockBreakEvent : BlockExpEvent, Cancellable /// The Player that is breaking the block involved in this event. public Player getPlayer() => _player; - /// - /// Gets the cancellation state of this event. - /// - /// true if this event is cancelled. + /// public bool isCancelled() => _cancel; - /// - /// Sets the cancellation state of this event. A cancelled event will not be - /// executed in the server, but will still pass to other plugins. - /// - /// true if you wish to cancel this event. + /// public void setCancelled(bool cancel) { _cancel = cancel; diff --git a/Minecraft.Server.FourKit/Event/Block/BlockBurnEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockBurnEvent.cs new file mode 100644 index 000000000..3745bae37 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockBurnEvent.cs @@ -0,0 +1,30 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Called when a block is destroyed as a result of being burnt by fire. +/// +/// If a Block Burn event is cancelled, the block will not be destroyed +/// as a result of being burnt by fire. +/// +public class BlockBurnEvent : BlockEvent, Cancellable +{ + private bool _cancel; + + internal BlockBurnEvent(Block block) : base(block) + { + _cancel = false; + } + + + /// + public bool isCancelled() => _cancel; + + + /// + public void setCancelled(bool cancel) + { + _cancel = cancel; + } +} diff --git a/Minecraft.Server.FourKit/Event/Block/BlockFormEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockFormEvent.cs new file mode 100644 index 000000000..d94a9df43 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockFormEvent.cs @@ -0,0 +1,23 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Called when a block is formed or spreads based on world conditions. +/// Use to catch blocks that actually spread +/// and don't just "randomly" form. +/// +/// Examples: +/// +/// Snow forming due to a snow storm. +/// Ice forming in a snowy Biome like Taiga or Tundra. +/// +/// +/// If a Block Form event is cancelled, the block will not be formed. +/// +public class BlockFormEvent : BlockGrowEvent, Cancellable +{ + internal BlockFormEvent(Block block, BlockState newState) : base(block, newState) + { + } +} diff --git a/Minecraft.Server.FourKit/Event/Block/BlockFromToEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockFromToEvent.cs new file mode 100644 index 000000000..b75a24b4a --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockFromToEvent.cs @@ -0,0 +1,59 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Represents events with a source block and a destination block, currently +/// only applies to liquid (lava and water) and teleporting dragon eggs. +/// +/// If a Block From To event is cancelled, the block will not move +/// (the liquid will not flow). +/// +public class BlockFromToEvent : BlockEvent, Cancellable +{ + private readonly Block _to; + private readonly BlockFace _face; + private bool _cancel; + + internal BlockFromToEvent(Block block, BlockFace face) : base(block) + { + _face = face; + _to = block.getRelative(face); + _cancel = false; + } + + internal BlockFromToEvent(Block block, Block toBlock) : base(block) + { + _to = toBlock; + _face = BlockFace.SELF; + _cancel = false; + } + + internal BlockFromToEvent(Block block, Block toBlock, BlockFace face) : base(block) + { + _to = toBlock; + _face = face; + _cancel = false; + } + + /// + /// Gets the BlockFace that the block is moving to. + /// + /// The BlockFace that the block is moving to. + public BlockFace getFace() => _face; + + /// + /// Convenience method for getting the faced Block. + /// + /// The faced Block. + public Block getToBlock() => _to; + + /// + public bool isCancelled() => _cancel; + + /// + public void setCancelled(bool cancel) + { + _cancel = cancel; + } +} diff --git a/Minecraft.Server.FourKit/Event/Block/BlockGrowEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockGrowEvent.cs new file mode 100644 index 000000000..5c4905d11 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockGrowEvent.cs @@ -0,0 +1,44 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Called when a block grows naturally in the world. +/// +/// Examples: +/// +/// Wheat +/// Sugar Cane +/// Cactus +/// Watermelon +/// Pumpkin +/// +/// +/// If a Block Grow event is cancelled, the block will not grow. +/// +public class BlockGrowEvent : BlockEvent, Cancellable +{ + private bool _cancel; + private readonly BlockState _newState; + + internal BlockGrowEvent(Block block, BlockState newState) : base(block) + { + _cancel = false; + _newState = newState; + } + + /// + /// Gets the state of the block where it will form or spread to. + /// + /// The block state for this events block. + public BlockState getNewState() => _newState; + + /// + public bool isCancelled() => _cancel; + + /// + public void setCancelled(bool cancel) + { + _cancel = cancel; + } +} diff --git a/Minecraft.Server.FourKit/Event/Block/BlockPistonEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockPistonEvent.cs new file mode 100644 index 000000000..a8e4d9b3b --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockPistonEvent.cs @@ -0,0 +1,45 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Called when a piston block is triggered. +/// +public abstract class BlockPistonEvent : BlockEvent, Cancellable +{ + private bool _cancel; + private readonly BlockFace _direction; + + internal protected BlockPistonEvent(Block block, BlockFace direction) : base(block) + { + _direction = direction; + _cancel = false; + } + + + /// + public bool isCancelled() => _cancel; + + + /// + public void setCancelled(bool cancelled) + { + _cancel = cancelled; + } + + /// + /// Returns true if the Piston in the event is sticky. + /// + /// Stickiness of the piston. + public bool isSticky() + { + var type = getBlock().getType(); + return type == Material.PISTON_STICKY_BASE; + } + + /// + /// Return the direction in which the piston will operate. + /// + /// Direction of the piston. + public BlockFace getDirection() => _direction; +} diff --git a/Minecraft.Server.FourKit/Event/Block/BlockPistonExtendEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockPistonExtendEvent.cs new file mode 100644 index 000000000..f0e88110c --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockPistonExtendEvent.cs @@ -0,0 +1,46 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Called when a piston extends. +/// +public class BlockPistonExtendEvent : BlockPistonEvent +{ + private readonly int _length; + + internal BlockPistonExtendEvent(Block block, int length, BlockFace direction) + : base(block, direction) + { + _length = length; + } + + /// + /// Get the amount of blocks which will be moved while extending. + /// + /// The amount of moving blocks. + public int getLength() => _length; + + /// + /// Get an immutable list of the blocks which will be moved by the extending. + /// + /// Immutable list of the moved blocks. + public List getBlocks() + { + var blocks = new List(); + var world = getBlock().getWorld(); + int x = getBlock().getX(); + int y = getBlock().getY(); + int z = getBlock().getZ(); + var dir = getDirection(); + + for (int i = 0; i < _length; i++) + { + x += dir.getModX(); + y += dir.getModY(); + z += dir.getModZ(); + blocks.Add(new Block(world, x, y, z)); + } + return blocks.AsReadOnly().ToList(); + } +} diff --git a/Minecraft.Server.FourKit/Event/Block/BlockPistonRetractEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockPistonRetractEvent.cs new file mode 100644 index 000000000..450f8c2c7 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockPistonRetractEvent.cs @@ -0,0 +1,30 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Called when a piston retracts. +/// +public class BlockPistonRetractEvent : BlockPistonEvent +{ + internal BlockPistonRetractEvent(Block block, BlockFace direction) + : base(block, direction) + { + } + + /// + /// Gets the location where the possible moving block might be if the + /// retracting piston is sticky. + /// + /// The possible location of the possibly moving block. + public Location getRetractLocation() + { + var block = getBlock(); + var dir = getDirection(); + return new Location( + block.getWorld(), + block.getX() + dir.getModX() * 2, + block.getY() + dir.getModY() * 2, + block.getZ() + dir.getModZ() * 2); + } +} diff --git a/Minecraft.Server.FourKit/Event/Block/BlockSpreadEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockSpreadEvent.cs new file mode 100644 index 000000000..1f8f8d71a --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockSpreadEvent.cs @@ -0,0 +1,32 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Called when a block spreads based on world conditions. +/// Use to catch blocks that "randomly" form +/// instead of actually spread. +/// +/// Examples: +/// +/// Mushrooms spreading. +/// Fire spreading. +/// +/// +/// If a Block Spread event is cancelled, the block will not spread. +/// +public class BlockSpreadEvent : BlockFormEvent, Cancellable +{ + private readonly Block _source; + + internal BlockSpreadEvent(Block block, Block source, BlockState newState) : base(block, newState) + { + _source = source; + } + + /// + /// Gets the source block involved in this event. + /// + /// The Block for the source block involved in this event. + public Block getSource() => _source; +} diff --git a/Minecraft.Server.FourKit/Event/Entity/EntityDamageEvent.cs b/Minecraft.Server.FourKit/Event/Entity/EntityDamageEvent.cs index fb1a5e574..52a553756 100644 --- a/Minecraft.Server.FourKit/Event/Entity/EntityDamageEvent.cs +++ b/Minecraft.Server.FourKit/Event/Entity/EntityDamageEvent.cs @@ -90,17 +90,12 @@ public class EntityDamageEvent : EntityEvent, Cancellable /// The amount of damage after reduction. public double getFinalDamage() => _finalDamage; - /// - /// Gets the cancellation state of this event. - /// - /// true if this event is cancelled. + + /// public bool isCancelled() => _cancel; - /// - /// Sets the cancellation state of this event. A cancelled event will not - /// be executed in the server, but will still pass to other plugins. - /// - /// true if you wish to cancel this event. + + /// public void setCancelled(bool cancel) { _cancel = cancel; diff --git a/Minecraft.Server.FourKit/Event/Inventory/InventoryInteractEvent.cs b/Minecraft.Server.FourKit/Event/Inventory/InventoryInteractEvent.cs index 4d5873e6e..bdc3b89b9 100644 --- a/Minecraft.Server.FourKit/Event/Inventory/InventoryInteractEvent.cs +++ b/Minecraft.Server.FourKit/Event/Inventory/InventoryInteractEvent.cs @@ -24,17 +24,11 @@ public abstract class InventoryInteractEvent : InventoryEvent, Cancellable return transaction.getPlayer(); } - /// - /// Gets the cancellation state of this event. A cancelled event will not - /// be executed in the server, but will still pass to other plugins. - /// - /// true if this event is cancelled. + + /// public bool isCancelled() => _cancelled; - /// - /// Sets the cancellation state of this event. A cancelled event will not - /// be executed in the server, but will still pass to other plugins. - /// - /// true if you wish to cancel this event. + + /// public void setCancelled(bool cancel) => _cancelled = cancel; } diff --git a/Minecraft.Server.FourKit/Event/Player/PlayerCommandPreprocessEvent.cs b/Minecraft.Server.FourKit/Event/Player/PlayerCommandPreprocessEvent.cs new file mode 100644 index 000000000..dd20a758c --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Player/PlayerCommandPreprocessEvent.cs @@ -0,0 +1,53 @@ +namespace Minecraft.Server.FourKit.Event.Player; + +using Minecraft.Server.FourKit.Entity; + +/// +/// Called early in the command handling process. This event is only for very +/// exceptional cases and you should not normally use it. +/// +/// If a PlayerCommandPreprocessEvent is cancelled, the command will not +/// be executed in the server, but will still pass to other plugins. +/// +public class PlayerCommandPreprocessEvent : PlayerEvent, Cancellable +{ + private string _message; + private bool _cancel; + + internal PlayerCommandPreprocessEvent(Player player, string message) : base(player) + { + _message = message; + _cancel = false; + } + + /// + public bool isCancelled() => _cancel; + + /// + public void setCancelled(bool cancel) + { + _cancel = cancel; + } + + /// + /// Gets the command that the player is attempting to send. All commands + /// begin with a special character; implementations do not consider the + /// first character when executing the content. + /// + /// Message the player is attempting to send. + public string getMessage() => _message; + + /// + /// Sets the command that the player will send. All commands begin with a + /// special character; implementations do not consider the first character + /// when executing the content. + /// + /// New message that the player will send. + /// If command is null or empty. + public void setMessage(string command) + { + if (string.IsNullOrEmpty(command)) + throw new ArgumentException("Command may not be null or empty", nameof(command)); + _message = command; + } +} diff --git a/Minecraft.Server.FourKit/FourKitHost.Events.cs b/Minecraft.Server.FourKit/FourKitHost.Events.cs index 165986681..08329ebd0 100644 --- a/Minecraft.Server.FourKit/FourKitHost.Events.cs +++ b/Minecraft.Server.FourKit/FourKitHost.Events.cs @@ -871,6 +871,51 @@ public static partial class FourKitHost } } + [UnmanagedCallersOnly] + public static int FireCommandPreprocess(int entityId, IntPtr cmdUtf8, int cmdByteLen, IntPtr outBuf, int outBufSize, IntPtr outLenPtr) + { + try + { + var player = FourKit.GetPlayerByEntityId(entityId); + if (player == null) + return 0; + + SyncPlayerFromNative(player); + + string commandLine = cmdByteLen > 0 + ? Marshal.PtrToStringUTF8(cmdUtf8, cmdByteLen) ?? string.Empty + : string.Empty; + + if (string.IsNullOrEmpty(commandLine)) + return 0; + + var preEvt = new Event.Player.PlayerCommandPreprocessEvent(player, commandLine); + FourKit.FireEvent(preEvt); + + if (preEvt.isCancelled()) + return 1; + + string modified = preEvt.getMessage(); + if (modified != commandLine && outBuf != IntPtr.Zero && outBufSize > 0) + { + byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(modified); + if (utf8Bytes.Length < outBufSize) + { + Marshal.Copy(utf8Bytes, 0, outBuf, utf8Bytes.Length); + if (outLenPtr != IntPtr.Zero) + Marshal.WriteInt32(outLenPtr, utf8Bytes.Length); + } + } + + return 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FireCommandPreprocess error: {ex}"); + return 0; + } + } + [UnmanagedCallersOnly] public static int HandleConsoleCommand(IntPtr cmdUtf8, int cmdByteLen) { @@ -1065,4 +1110,144 @@ public static partial class FourKitHost ServerLog.Error("fourkit", $"FireBedLeave error: {ex}"); } } + + [UnmanagedCallersOnly] + public static int FireBlockGrow(int dimId, int x, int y, int z, int newTileId, int newTileData) + { + try + { + var world = FourKit.getWorld(dimId); + var block = new Block.Block(world, x, y, z); + var newState = new Block.BlockState(world, x, y, z, newTileId, newTileData); + var evt = new Event.Block.BlockGrowEvent(block, newState); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FireBlockGrow error: {ex}"); + return 0; + } + } + + [UnmanagedCallersOnly] + public static int FireBlockForm(int dimId, int x, int y, int z, int newTileId, int newTileData) + { + try + { + var world = FourKit.getWorld(dimId); + var block = new Block.Block(world, x, y, z); + var newState = new Block.BlockState(world, x, y, z, newTileId, newTileData); + var evt = new Event.Block.BlockFormEvent(block, newState); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FireBlockForm error: {ex}"); + return 0; + } + } + + [UnmanagedCallersOnly] + public static int FireBlockBurn(int dimId, int x, int y, int z) + { + try + { + var world = FourKit.getWorld(dimId); + var block = new Block.Block(world, x, y, z); + var evt = new Event.Block.BlockBurnEvent(block); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FireBlockBurn error: {ex}"); + return 0; + } + } + + [UnmanagedCallersOnly] + public static int FireBlockSpread(int dimId, int x, int y, int z, int srcX, int srcY, int srcZ, int newTileId, int newTileData) + { + try + { + var world = FourKit.getWorld(dimId); + var block = new Block.Block(world, x, y, z); + var source = new Block.Block(world, srcX, srcY, srcZ); + var newState = new Block.BlockState(world, x, y, z, newTileId, newTileData); + var evt = new Event.Block.BlockSpreadEvent(block, source, newState); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FireBlockSpread error: {ex}"); + return 0; + } + } + + [UnmanagedCallersOnly] + public static int FirePistonExtend(int dimId, int x, int y, int z, int direction, int length) + { + try + { + var world = FourKit.getWorld(dimId); + var block = new Block.Block(world, x, y, z); + var face = Enum.IsDefined(typeof(Block.BlockFace), direction) + ? (Block.BlockFace)direction + : Block.BlockFace.SELF; + var evt = new Event.Block.BlockPistonExtendEvent(block, length, face); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FirePistonExtend error: {ex}"); + return 0; + } + } + + [UnmanagedCallersOnly] + public static int FirePistonRetract(int dimId, int x, int y, int z, int direction) + { + try + { + var world = FourKit.getWorld(dimId); + var block = new Block.Block(world, x, y, z); + var face = Enum.IsDefined(typeof(Block.BlockFace), direction) + ? (Block.BlockFace)direction + : Block.BlockFace.SELF; + var evt = new Event.Block.BlockPistonRetractEvent(block, face); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FirePistonRetract error: {ex}"); + return 0; + } + } + + [UnmanagedCallersOnly] + public static int FireBlockFromTo(int dimId, int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int face) + { + try + { + var world = FourKit.getWorld(dimId); + var from = new Block.Block(world, fromX, fromY, fromZ); + var to = new Block.Block(world, toX, toY, toZ); + var blockFace = Enum.IsDefined(typeof(Block.BlockFace), face) + ? (Block.BlockFace)face + : Block.BlockFace.SELF; + var evt = new Event.Block.BlockFromToEvent(from, to, blockFace); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FireBlockFromTo error: {ex}"); + return 0; + } + } } diff --git a/Minecraft.Server.FourKit/Location.cs b/Minecraft.Server.FourKit/Location.cs index 478bb954a..6b8e314af 100644 --- a/Minecraft.Server.FourKit/Location.cs +++ b/Minecraft.Server.FourKit/Location.cs @@ -187,5 +187,5 @@ public class Location public Location clone() => new Location(LocationWorld, X, Y, Z, Yaw, Pitch); /// - public override string ToString() => $"Location(world={LocationWorld}, x={X}, y={Y}, z={Z}, yaw={Yaw}, pitch={Pitch})"; + public override string ToString() => $"Location(world={LocationWorld.getName()}, x={X}, y={Y}, z={Z}, yaw={Yaw}, pitch={Pitch})"; } diff --git a/Minecraft.Server.FourKit/docs/usage-of-all-events.md b/Minecraft.Server.FourKit/docs/usage-of-all-events.md index 7103fd7f6..66c272121 100644 --- a/Minecraft.Server.FourKit/docs/usage-of-all-events.md +++ b/Minecraft.Server.FourKit/docs/usage-of-all-events.md @@ -462,6 +462,56 @@ public void onBedLeave(PlayerBedLeaveEvent e) --- +@subsection playercommandpreprocessevent PlayerCommandPreprocessEvent + +\ref Minecraft.Server.FourKit.Event.Player.PlayerCommandPreprocessEvent "PlayerCommandPreprocessEvent" is fired early in the command handling process when a player sends a command. You can modify the command, cancel it, or use it for logging. This fires before the command is dispatched to any command handler. + +```csharp +[EventHandler] +public void onCommand(PlayerCommandPreprocessEvent e) +{ + // log all commands + Console.WriteLine(e.getPlayer().getName() + " issued command: " + e.getMessage()); +} +``` + +```csharp +[EventHandler] +public void onCommand(PlayerCommandPreprocessEvent e) +{ + // block a specific command + if (e.getMessage().StartsWith("/secret")) + { + e.setCancelled(true); + e.getPlayer().sendMessage("That command is disabled!"); + } +} +``` + +```csharp +[EventHandler] +public void onCommand(PlayerCommandPreprocessEvent e) +{ + // redirect a command alias + if (e.getMessage().StartsWith("/gm ")) + { + e.setMessage("/gamemode " + e.getMessage().Substring(4)); + } +} +``` + +| Method | Description | +|--------|-------------| +| `getPlayer()` | The player who issued the command. | +| `getMessage()` | The full command string the player is sending. | +| `setMessage(string)` | Change the command that will be processed. | +| `isCancelled()` | Whether the command is cancelled. | +| `setCancelled(bool)` | Cancel or allow the command. | + +> **Cancellable:** Yes + +--- + @section entity_events Entity Events @subsection entitydamageevent EntityDamageEvent @@ -745,6 +795,259 @@ public void onSign(SignChangeEvent e) --- + +--- + +@subsection blockgrowevent BlockGrowEvent + +\ref Minecraft.Server.FourKit.Event.Block.BlockGrowEvent "BlockGrowEvent" is fired when a block grows naturally in the world. This includes crops (wheat, nether wart, cocoa beans), sugar cane, cactus, and melon/pumpkin fruit placement. + +```csharp +[EventHandler] +public void onGrow(BlockGrowEvent e) +{ + // prevent all crop growth + e.setCancelled(true); +} +``` + +```csharp +[EventHandler] +public void onGrow(BlockGrowEvent e) +{ + // log growth events + var b = e.getBlock(); + Console.WriteLine($"Block grew at {b.getX()}, {b.getY()}, {b.getZ()} type={b.getType()}"); +} +``` + +| Method | Description | +|--------|-------------| +| `getBlock()` | The `Block` that is growing. | +| `getNewState()` | The `BlockState` representing what the block will become. | +| `isCancelled()` | Whether the growth is cancelled. | +| `setCancelled(bool)` | Cancel or allow the growth. | + +> **Cancellable:** Yes + +--- + +@subsection blockformevent BlockFormEvent + +\ref Minecraft.Server.FourKit.Event.Block.BlockFormEvent "BlockFormEvent" extends `BlockGrowEvent` and is fired when a block forms due to world conditions. Examples include snow forming during a storm and ice forming in cold biomes. Use \ref Minecraft.Server.FourKit.Event.Block.BlockSpreadEvent "BlockSpreadEvent" to catch blocks that actually spread instead of randomly forming. + +```csharp +[EventHandler] +public void onForm(BlockFormEvent e) +{ + // prevent snow and ice from forming + e.setCancelled(true); +} +``` + +| Method | Description | +|--------|-------------| +| `getBlock()` | The `Block` that is forming. | +| `getNewState()` | The `BlockState` representing what the block will become. | +| `isCancelled()` | Whether the formation is cancelled. | +| `setCancelled(bool)` | Cancel or allow the formation. | + +> **Cancellable:** Yes (inherited from `BlockGrowEvent`) + +--- + +@subsection blockspreadevent BlockSpreadEvent + +\ref Minecraft.Server.FourKit.Event.Block.BlockSpreadEvent "BlockSpreadEvent" extends `BlockFormEvent` and is fired when a block spreads from one location to another. Examples include fire spreading, mushrooms spreading, and grass spreading to dirt. + +```csharp +[EventHandler] +public void onSpread(BlockSpreadEvent e) +{ + // prevent fire from spreading + if (e.getSource().getType() == Material.FIRE) + { + e.setCancelled(true); + } +} +``` + +```csharp +[EventHandler] +public void onSpread(BlockSpreadEvent e) +{ + // prevent grass from spreading + if (e.getBlock().getType() == Material.GRASS) + { + e.setCancelled(true); + } +} +``` + +| Method | Description | +|--------|-------------| +| `getBlock()` | The `Block` where the spread is occurring (destination). | +| `getSource()` | The source `Block` that is spreading. | +| `getNewState()` | The `BlockState` representing what the block will become. | +| `isCancelled()` | Whether the spread is cancelled. | +| `setCancelled(bool)` | Cancel or allow the spread. | + +> **Cancellable:** Yes (inherited) + +--- + +@subsection blockburnevent BlockBurnEvent + +\ref Minecraft.Server.FourKit.Event.Block.BlockBurnEvent "BlockBurnEvent" is fired when a block is destroyed as a result of being burnt by fire. + +```csharp +[EventHandler] +public void onBurn(BlockBurnEvent e) +{ + // prevent wooden planks from burning + if (e.getBlock().getType() == Material.WOOD) + { + e.setCancelled(true); + } +} +``` + +```csharp +[EventHandler] +public void onBurn(BlockBurnEvent e) +{ + // prevent all fire destruction + e.setCancelled(true); +} +``` + +| Method | Description | +|--------|-------------| +| `getBlock()` | The `Block` that is being burnt. | +| `isCancelled()` | Whether the burn is cancelled. | +| `setCancelled(bool)` | Cancel or allow the burn. | + +> **Cancellable:** Yes + +--- + +@subsection blockfromtoevent BlockFromToEvent + +\ref Minecraft.Server.FourKit.Event.Block.BlockFromToEvent "BlockFromToEvent" is fired when a block moves from one location to another. This currently only applies to liquid flow (lava and water) and teleporting dragon eggs. + +```csharp +[EventHandler] +public void onFromTo(BlockFromToEvent e) +{ + // prevent water from flowing + if (e.getBlock().getType() == Material.WATER || e.getBlock().getType() == Material.STATIONARY_WATER) + { + e.setCancelled(true); + } +} +``` + +```csharp +[EventHandler] +public void onFromTo(BlockFromToEvent e) +{ + // log liquid flow + var from = e.getBlock(); + var to = e.getToBlock(); + Console.WriteLine($"Block moving from {from.getX()},{from.getY()},{from.getZ()} to {to.getX()},{to.getY()},{to.getZ()}"); +} +``` + +| Method | Description | +|--------|-------------| +| `getBlock()` | The source `Block` that is moving. | +| `getToBlock()` | The destination `Block`. | +| `getFace()` | The `BlockFace` direction the block is moving to. | +| `isCancelled()` | Whether the move is cancelled. | +| `setCancelled(bool)` | Cancel or allow the move. | + +> **Cancellable:** Yes + +--- + +@subsection blockpistonextendevent BlockPistonExtendEvent + +\ref Minecraft.Server.FourKit.Event.Block.BlockPistonExtendEvent "BlockPistonExtendEvent" extends `BlockPistonEvent` and is fired when a piston extends. You can inspect the piston direction, stickiness, number of blocks being pushed, and cancel the extension. + +```csharp +[EventHandler] +public void onPiston(BlockPistonExtendEvent e) +{ + // prevent sticky pistons from extending + if (e.isSticky()) + { + e.setCancelled(true); + } +} +``` + +```csharp +[EventHandler] +public void onPiston(BlockPistonExtendEvent e) +{ + // log piston activity + Console.WriteLine($"Piston extending {e.getDirection()} pushing {e.getLength()} blocks"); +} +``` + +| Method | Description | +|--------|-------------| +| `getBlock()` | The piston `Block`. | +| `getDirection()` | The `BlockFace` direction the piston is extending. | +| `isSticky()` | Whether the piston is a sticky piston. | +| `getLength()` | The number of blocks being pushed. | +| `getBlocks()` | List of `Block` objects that will be moved. | +| `isCancelled()` | Whether the extension is cancelled. | +| `setCancelled(bool)` | Cancel or allow the extension. | + +> **Cancellable:** Yes + +--- + +@subsection blockpistonretractevent BlockPistonRetractEvent + +\ref Minecraft.Server.FourKit.Event.Block.BlockPistonRetractEvent "BlockPistonRetractEvent" extends `BlockPistonEvent` and is fired when a piston retracts. For sticky pistons, the retract location indicates where the block being pulled is located. + +```csharp +[EventHandler] +public void onPiston(BlockPistonRetractEvent e) +{ + // prevent all sticky piston retractions + if (e.isSticky()) + { + e.setCancelled(true); + } +} +``` + +```csharp +[EventHandler] +public void onPiston(BlockPistonRetractEvent e) +{ + // log where the retract is pulling from + var loc = e.getRetractLocation(); + Console.WriteLine($"Piston retracting, pull location: {loc.getBlockX()}, {loc.getBlockY()}, {loc.getBlockZ()}"); +} +``` + +| Method | Description | +|--------|-------------| +| `getBlock()` | The piston `Block`. | +| `getDirection()` | The `BlockFace` direction the piston is retracting. | +| `isSticky()` | Whether the piston is a sticky piston. | +| `getRetractLocation()` | The `Location` of the block that may be pulled (for sticky pistons). | +| `isCancelled()` | Whether the retraction is cancelled. | +| `setCancelled(bool)` | Cancel or allow the retraction. | + +> **Cancellable:** Yes + +--- + @section inventory_events Inventory Events @subsection inventoryopenevent InventoryOpenEvent diff --git a/Minecraft.Server/FourKitBridge.cpp b/Minecraft.Server/FourKitBridge.cpp index 220e694a4..81c24111f 100644 --- a/Minecraft.Server/FourKitBridge.cpp +++ b/Minecraft.Server/FourKitBridge.cpp @@ -95,6 +95,14 @@ typedef void(__stdcall *fn_set_entity_callbacks)(void *setSneaking, void *setVel typedef void(__stdcall *fn_set_experience_callbacks)(void *setLevel, void *setExp, void *giveExp, void *giveExpLevels, void *setFoodLevel, void *setSaturation, void *setExhaustion); typedef void(__stdcall *fn_set_particle_callbacks)(void *spawnParticle); typedef void(__stdcall *fn_set_vehicle_callbacks)(void *setPassenger, void *leaveVehicle, void *eject, void *getVehicleId, void *getPassengerId, void *getEntityInfo); +typedef int(__stdcall *fn_fire_block_grow)(int dimId, int x, int y, int z, int newTileId, int newTileData); +typedef int(__stdcall *fn_fire_block_form)(int dimId, int x, int y, int z, int newTileId, int newTileData); +typedef int(__stdcall *fn_fire_block_burn)(int dimId, int x, int y, int z); +typedef int(__stdcall *fn_fire_block_spread)(int dimId, int x, int y, int z, int srcX, int srcY, int srcZ, int newTileId, int newTileData); +typedef int(__stdcall *fn_fire_piston_extend)(int dimId, int x, int y, int z, int direction, int length); +typedef int(__stdcall *fn_fire_piston_retract)(int dimId, int x, int y, int z, int direction); +typedef int(__stdcall *fn_fire_command_preprocess)(int entityId, const char *cmdUtf8, int cmdByteLen, char *outBuf, int outBufSize, int *outLen); +typedef int(__stdcall *fn_fire_block_from_to)(int dimId, int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int face); struct OpenContainerInfo { @@ -144,6 +152,14 @@ static fn_set_entity_callbacks s_managedSetEntityCallbacks = nullptr; static fn_set_experience_callbacks s_managedSetExperienceCallbacks = nullptr; static fn_set_particle_callbacks s_managedSetParticleCallbacks = nullptr; static fn_set_vehicle_callbacks s_managedSetVehicleCallbacks = nullptr; +static fn_fire_block_grow s_managedFireBlockGrow = nullptr; +static fn_fire_block_form s_managedFireBlockForm = nullptr; +static fn_fire_block_burn s_managedFireBlockBurn = nullptr; +static fn_fire_block_spread s_managedFireBlockSpread = nullptr; +static fn_fire_piston_extend s_managedFirePistonExtend = nullptr; +static fn_fire_piston_retract s_managedFirePistonRetract = nullptr; +static fn_fire_command_preprocess s_managedFireCommandPreprocess = nullptr; +static fn_fire_block_from_to s_managedFireBlockFromTo = nullptr; static bool s_initialized = false; @@ -214,6 +230,14 @@ void Initialize() {L"SetExperienceCallbacks", (void **)&s_managedSetExperienceCallbacks}, {L"SetParticleCallbacks", (void **)&s_managedSetParticleCallbacks}, {L"SetVehicleCallbacks", (void **)&s_managedSetVehicleCallbacks}, + {L"FireBlockGrow", (void **)&s_managedFireBlockGrow}, + {L"FireBlockForm", (void **)&s_managedFireBlockForm}, + {L"FireBlockBurn", (void **)&s_managedFireBlockBurn}, + {L"FireBlockSpread", (void **)&s_managedFireBlockSpread}, + {L"FirePistonExtend", (void **)&s_managedFirePistonExtend}, + {L"FirePistonRetract", (void **)&s_managedFirePistonRetract}, + {L"FireCommandPreprocess", (void **)&s_managedFireCommandPreprocess}, + {L"FireBlockFromTo", (void **)&s_managedFireBlockFromTo}, }; bool ok = true; @@ -902,4 +926,88 @@ void FireBedLeave(int entityId, int dimId, int bedX, int bedY, int bedZ) return; s_managedFireBedLeave(entityId, dimId, bedX, bedY, bedZ); } + +bool FireBlockGrow(int dimId, int x, int y, int z, int newTileId, int newTileData) +{ + if (!s_initialized || !s_managedFireBlockGrow) + return false; + return s_managedFireBlockGrow(dimId, x, y, z, newTileId, newTileData) != 0; +} + +bool FireBlockForm(int dimId, int x, int y, int z, int newTileId, int newTileData) +{ + if (!s_initialized || !s_managedFireBlockForm) + return false; + return s_managedFireBlockForm(dimId, x, y, z, newTileId, newTileData) != 0; +} + +bool FireBlockBurn(int dimId, int x, int y, int z) +{ + if (!s_initialized || !s_managedFireBlockBurn) + return false; + return s_managedFireBlockBurn(dimId, x, y, z) != 0; +} + +bool FireBlockSpread(int dimId, int x, int y, int z, int srcX, int srcY, int srcZ, int newTileId, int newTileData) +{ + if (!s_initialized || !s_managedFireBlockSpread) + return false; + return s_managedFireBlockSpread(dimId, x, y, z, srcX, srcY, srcZ, newTileId, newTileData) != 0; +} + +bool FirePistonExtend(int dimId, int x, int y, int z, int direction, int length) +{ + if (!s_initialized || !s_managedFirePistonExtend) + return false; + return s_managedFirePistonExtend(dimId, x, y, z, direction, length) != 0; +} + +bool FirePistonRetract(int dimId, int x, int y, int z, int direction) +{ + if (!s_initialized || !s_managedFirePistonRetract) + return false; + return s_managedFirePistonRetract(dimId, x, y, z, direction) != 0; +} + +bool FireCommandPreprocess(int entityId, const std::wstring &commandLine, std::wstring &outCommand) +{ + if (!s_initialized || !s_managedFireCommandPreprocess) + { + return false; + } + + std::string cmdUtf8 = ServerRuntime::StringUtils::WideToUtf8(commandLine); + + const int kBufSize = 2048; + char outBuf[kBufSize] = {}; + int outLen = 0; + + int cancelled = s_managedFireCommandPreprocess(entityId, + cmdUtf8.empty() ? "" : cmdUtf8.data(), (int)cmdUtf8.size(), + outBuf, kBufSize, &outLen); + + if (cancelled != 0) + { + return true; + } + + if (outLen > 0 && outLen < kBufSize) + { + std::string resultUtf8(outBuf, outLen); + outCommand = ServerRuntime::StringUtils::Utf8ToWide(resultUtf8); + } + else + { + outCommand = commandLine; + } + + return false; +} + +bool FireBlockFromTo(int dimId, int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int face) +{ + if (!s_initialized || !s_managedFireBlockFromTo) + return false; + return s_managedFireBlockFromTo(dimId, fromX, fromY, fromZ, toX, toY, toZ, face) != 0; +} } // namespace FourKitBridge diff --git a/Minecraft.Server/FourKitBridge.h b/Minecraft.Server/FourKitBridge.h index 99c64b917..98894bf63 100644 --- a/Minecraft.Server/FourKitBridge.h +++ b/Minecraft.Server/FourKitBridge.h @@ -84,4 +84,12 @@ namespace FourKitBridge int FireInventoryClick(int entityId, int slot, int button, int clickType); bool FireBedEnter(int entityId, int dimId, int bedX, int bedY, int bedZ); void FireBedLeave(int entityId, int dimId, int bedX, int bedY, int bedZ); + bool FireBlockGrow(int dimId, int x, int y, int z, int newTileId, int newTileData); + bool FireBlockForm(int dimId, int x, int y, int z, int newTileId, int newTileData); + bool FireBlockBurn(int dimId, int x, int y, int z); + bool FireBlockSpread(int dimId, int x, int y, int z, int srcX, int srcY, int srcZ, int newTileId, int newTileData); + bool FirePistonExtend(int dimId, int x, int y, int z, int direction, int length); + bool FirePistonRetract(int dimId, int x, int y, int z, int direction); + bool FireCommandPreprocess(int entityId, const std::wstring &commandLine, std::wstring &outCommand); + bool FireBlockFromTo(int dimId, int fromX, int fromY, int fromZ, int toX, int toY, int toZ, int face); } diff --git a/Minecraft.Server/cmake/sources/Common.cmake b/Minecraft.Server/cmake/sources/Common.cmake index bbdf570f4..031ab0548 100644 --- a/Minecraft.Server/cmake/sources/Common.cmake +++ b/Minecraft.Server/cmake/sources/Common.cmake @@ -512,6 +512,17 @@ set(_MINECRAFT_SERVER_COMMON_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/Sapling.h" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/ConsoleSaveFileOriginal.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/ConsoleSaveFileOriginal.h" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/CactusTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/CocoaTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/CropTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/FireTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/GrassTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/PistonBaseTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/ReedTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/StemTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/NetherWartTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/LiquidTileDynamic.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/EggTile.cpp" "${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/CactusTile.cpp b/Minecraft.World/CactusTile.cpp index c306f11a9..445d2752d 100644 --- a/Minecraft.World/CactusTile.cpp +++ b/Minecraft.World/CactusTile.cpp @@ -7,6 +7,10 @@ #include "net.minecraft.h" #include "net.minecraft.world.h" #include "CactusTile.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif CactusTile::CactusTile(int id) : Tile(id, Material::cactus,isSolidRender()) { @@ -29,6 +33,10 @@ void CactusTile::tick(Level *level, int x, int y, int z, Random *random) int age = level->getData(x, y, z); if (age == 15) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockGrow(level->dimension->id, x, y + 1, z, id, 0)) + return; +#endif level->setTileAndUpdate(x, y + 1, z, id); level->setData(x, y, z, 0, Tile::UPDATE_NONE); neighborChanged(level, x, y + 1, z, id); diff --git a/Minecraft.World/CocoaTile.cpp b/Minecraft.World/CocoaTile.cpp index 3df58eb84..60b8b7fd8 100644 --- a/Minecraft.World/CocoaTile.cpp +++ b/Minecraft.World/CocoaTile.cpp @@ -5,6 +5,10 @@ #include "net.minecraft.world.h" #include "net.minecraft.h" #include "CocoaTile.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif const wstring CocoaTile::TEXTURE_AGES[] = { L"cocoa_0", L"cocoa_1", L"cocoa_2"}; @@ -40,6 +44,10 @@ void CocoaTile::tick(Level *level, int x, int y, int z, Random *random) int age = getAge(data); if (age < 2) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockGrow(level->dimension->id, x, y, z, level->getTile(x, y, z), ((age + 1) << 2) | getDirection(data))) + return; +#endif age++; level->setData(x, y, z, (age << 2) | (getDirection(data)), Tile::UPDATE_CLIENTS); } diff --git a/Minecraft.World/CropTile.cpp b/Minecraft.World/CropTile.cpp index 1a1611996..57541dd4b 100644 --- a/Minecraft.World/CropTile.cpp +++ b/Minecraft.World/CropTile.cpp @@ -4,6 +4,10 @@ #include "net.minecraft.world.item.h" #include "net.minecraft.world.h" #include "CropTile.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif CropTile::CropTile(int id) : Bush(id) { @@ -42,6 +46,10 @@ void CropTile::tick(Level *level, int x, int y, int z, Random *random) if (random->nextInt(static_cast(25 / growthSpeed) + 1) == 0) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockGrow(level->dimension->id, x, y, z, level->getTile(x, y, z), age + 1)) + return; +#endif age++; level->setData(x, y, z, age, Tile::UPDATE_CLIENTS); } diff --git a/Minecraft.World/EggTile.cpp b/Minecraft.World/EggTile.cpp index 7052fac9b..a0fd7ff7c 100644 --- a/Minecraft.World/EggTile.cpp +++ b/Minecraft.World/EggTile.cpp @@ -1,8 +1,12 @@ -#include "stdafx.h" + #include "stdafx.h" #include "EggTile.h" #include "net.minecraft.world.level.h" #include "net.minecraft.world.level.tile.h" #include "net.minecraft.world.entity.item.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif EggTile::EggTile(int id) : Tile(id, Material::egg, isSolidRender()) { @@ -70,11 +74,15 @@ void EggTile::teleport(Level *level, int x, int y, int z) int zt = z + level->random->nextInt(16) - level->random->nextInt(16); if (level->getTile(xt, yt, zt) == 0) { - // Fix for TU9: Content: Art: Dragon egg teleport particle effect isn't present. - // Don't set tiles on client, and don't create particles on the server (matches later change in Java) - if(!level->isClientSide) - { - level->setTileAndData(xt, yt, zt, id, level->getData(x, y, z), Tile::UPDATE_CLIENTS); + // Fix for TU9: Content: Art: Dragon egg teleport particle effect isn't present. + // Don't set tiles on client, and don't create particles on the server (matches later change in Java) + if(!level->isClientSide) + { + #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockFromTo(level->dimension->id, x, y, z, xt, yt, zt, 6 /*SELF*/)) + continue; + #endif + level->setTileAndData(xt, yt, zt, id, level->getData(x, y, z), Tile::UPDATE_CLIENTS); level->removeTile(x, y, z); // 4J Stu - The PC version is wrong as the particles calculated on the client side will point towards a different diff --git a/Minecraft.World/FireTile.cpp b/Minecraft.World/FireTile.cpp index 49f859b51..76f75cc56 100644 --- a/Minecraft.World/FireTile.cpp +++ b/Minecraft.World/FireTile.cpp @@ -7,6 +7,9 @@ #include "SoundTypes.h" #include "..\Minecraft.Client\MinecraftServer.h" #include "..\Minecraft.Client\PlayerList.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#endif // AP - added for Vita to set Alpha Cut out #include "IntBuffer.h" @@ -197,11 +200,16 @@ void FireTile::tick(Level *level, int x, int y, int z, Random *random) } if (odds > 0 && random->nextInt(rate) <= odds) { - if (!(level->isRaining() && level->isRainingAt(xx, yy, zz) || level->isRainingAt(xx - 1, yy, z) || level->isRainingAt(xx + 1, yy, zz) || level->isRainingAt(xx, yy, zz - 1) || level->isRainingAt(xx, yy, zz + 1))) - { - int tAge = age + random->nextInt(5) / 4; - if (tAge > 15) tAge = 15; - level->setTileAndData(xx, yy, zz, id, tAge, Tile::UPDATE_ALL); + if (!(level->isRaining() && level->isRainingAt(xx, yy, zz) || level->isRainingAt(xx - 1, yy, z) || level->isRainingAt(xx + 1, yy, zz) || level->isRainingAt(xx, yy, zz - 1) || level->isRainingAt(xx, yy, zz + 1))) + { + #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (!FourKitBridge::FireBlockSpread(level->dimension->id, xx, yy, zz, x, y, z, id, min(age + random->nextInt(5) / 4, 15))) + #endif + { + int tAge = age + random->nextInt(5) / 4; + if (tAge > 15) tAge = 15; + level->setTileAndData(xx, yy, zz, id, tAge, Tile::UPDATE_ALL); + } } } } @@ -221,6 +229,10 @@ void FireTile::checkBurnOut(Level *level, int x, int y, int z, int chance, Rando int odds = burnOdds[level->getTile(x, y, z)]; if (random->nextInt(chance) < odds) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockBurn(level->dimension->id, x, y, z)) + return; +#endif bool wasTnt = level->getTile(x, y, z) == Tile::tnt_Id; if (random->nextInt(age + 10) < 5 && !level->isRainingAt(x, y, z) && app.GetGameHostOption(eGameHostOption_FireSpreads)) { diff --git a/Minecraft.World/GrassTile.cpp b/Minecraft.World/GrassTile.cpp index 9be44d0eb..afa5ed26a 100644 --- a/Minecraft.World/GrassTile.cpp +++ b/Minecraft.World/GrassTile.cpp @@ -5,6 +5,10 @@ #include "net.minecraft.world.level.biome.h" #include "net.minecraft.h" #include "net.minecraft.world.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif // AP - included for PSVita Alpha cut out optimisation #include "IntBuffer.h" @@ -108,6 +112,9 @@ void GrassTile::tick(Level *level, int x, int y, int z, Random *random) int above = level->getTile(xt, yt + 1, zt); if (level->getTile(xt, yt, zt) == Tile::dirt_Id && level->getRawBrightness(xt, yt + 1, zt) >= MIN_BRIGHTNESS && Tile::lightBlock[above] <= 2) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (!FourKitBridge::FireBlockSpread(level->dimension->id, xt, yt, zt, x, y, z, Tile::grass_Id, 0)) +#endif level->setTileAndUpdate(xt, yt, zt, Tile::grass_Id); } } diff --git a/Minecraft.World/LiquidTileDynamic.cpp b/Minecraft.World/LiquidTileDynamic.cpp index 2c7c74f4b..30d18482c 100644 --- a/Minecraft.World/LiquidTileDynamic.cpp +++ b/Minecraft.World/LiquidTileDynamic.cpp @@ -2,6 +2,10 @@ #include "net.minecraft.world.level.h" #include "LiquidTileDynamic.h" #include "net.minecraft.world.level.dimension.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif LiquidTileDynamic::LiquidTileDynamic(int id, Material *material) : LiquidTile(id, material) { @@ -156,8 +160,8 @@ void LiquidTileDynamic::mainTick(Level *level, int x, int y, int z, Random *rand } } - if (depth >= 8) trySpreadTo(level, x, y - 1, z, depth); - else trySpreadTo(level, x, y - 1, z, depth + 8); + if (depth >= 8) trySpreadTo(level, x, y - 1, z, x, y, z, depth); + else trySpreadTo(level, x, y - 1, z, x, y, z, depth + 8); } else if (depth >= 0 && (depth == 0 || isWaterBlocking(level, x, y - 1, z))) { @@ -168,17 +172,31 @@ void LiquidTileDynamic::mainTick(Level *level, int x, int y, int z, Random *rand neighbor = 1; } if (neighbor >= 8) return; - if (spreads[0]) trySpreadTo(level, x - 1, y, z, neighbor); - if (spreads[1]) trySpreadTo(level, x + 1, y, z, neighbor); - if (spreads[2]) trySpreadTo(level, x, y, z - 1, neighbor); - if (spreads[3]) trySpreadTo(level, x, y, z + 1, neighbor); + if (spreads[0]) trySpreadTo(level, x - 1, y, z, x, y, z, neighbor); + if (spreads[1]) trySpreadTo(level, x + 1, y, z, x, y, z, neighbor); + if (spreads[2]) trySpreadTo(level, x, y, z - 1, x, y, z, neighbor); + if (spreads[3]) trySpreadTo(level, x, y, z + 1, x, y, z, neighbor); } } -void LiquidTileDynamic::trySpreadTo(Level *level, int x, int y, int z, int neighbor) +void LiquidTileDynamic::trySpreadTo(Level *level, int x, int y, int z, int fromX, int fromY, int fromZ, int neighbor) { if (canSpreadTo(level, x, y, z)) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + { + int face = 6; // SELF + int dx = x - fromX, dy = y - fromY, dz = z - fromZ; + if (dy < 0) face = 0; // DOWN + else if (dy > 0) face = 1; // UP + else if (dz < 0) face = 2; // NORTH + else if (dz > 0) face = 3; // SOUTH + else if (dx < 0) face = 4; // WEST + else if (dx > 0) face = 5; // EAST + if (FourKitBridge::FireBlockFromTo(level->dimension->id, fromX, fromY, fromZ, x, y, z, face)) + return; + } +#endif { int old = level->getTile(x, y, z); if (old > 0) diff --git a/Minecraft.World/LiquidTileDynamic.h b/Minecraft.World/LiquidTileDynamic.h index bf0c9a6d1..3d6d97c2e 100644 --- a/Minecraft.World/LiquidTileDynamic.h +++ b/Minecraft.World/LiquidTileDynamic.h @@ -36,7 +36,7 @@ private: public: void tick(Level *level, int x, int y, int z, Random *random); private: - void trySpreadTo(Level *level, int x, int y, int z, int neighbor); + void trySpreadTo(Level *level, int x, int y, int z, int fromX, int fromY, int fromZ, int neighbor); bool *result; int *dist; diff --git a/Minecraft.World/Mushroom.cpp b/Minecraft.World/Mushroom.cpp index 1ad2a2b38..91ba3f7aa 100644 --- a/Minecraft.World/Mushroom.cpp +++ b/Minecraft.World/Mushroom.cpp @@ -53,6 +53,9 @@ void Mushroom::tick(Level *level, int x, int y, int z, Random *random) if (level->isEmptyTile(x2, y2, z2) && canSurvive(level, x2, y2, z2)) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (!FourKitBridge::FireBlockSpread(level->dimension->id, x2, y2, z2, x, y, z, id, 0)) +#endif level->setTileAndData(x2, y2, z2, id, 0, UPDATE_CLIENTS); } } diff --git a/Minecraft.World/NetherWartTile.cpp b/Minecraft.World/NetherWartTile.cpp index afe45aec5..a7d8e21f1 100644 --- a/Minecraft.World/NetherWartTile.cpp +++ b/Minecraft.World/NetherWartTile.cpp @@ -4,6 +4,10 @@ #include "net.minecraft.world.level.biome.h" #include "net.minecraft.world.item.h" #include "net.minecraft.world.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif NetherWartTile::NetherWartTile(int id) : Bush(id) { @@ -36,6 +40,12 @@ void NetherWartTile::tick(Level *level, int x, int y, int z, Random *random) { if (random->nextInt(10) == 0) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockGrow(level->dimension->id, x, y, z, level->getTile(x, y, z), age + 1)) + { + return; + } +#endif age++; level->setData(x, y, z, age, Tile::UPDATE_CLIENTS); } diff --git a/Minecraft.World/PistonBaseTile.cpp b/Minecraft.World/PistonBaseTile.cpp index 530cbf73b..51519b1aa 100644 --- a/Minecraft.World/PistonBaseTile.cpp +++ b/Minecraft.World/PistonBaseTile.cpp @@ -10,6 +10,9 @@ #include "net.minecraft.world.h" #include "LevelChunk.h" #include "Dimension.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#endif const wstring PistonBaseTile::EDGE_TEX = L"piston_side"; const wstring PistonBaseTile::PLATFORM_TEX = L"piston_top"; @@ -230,6 +233,31 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, if (param1 == TRIGGER_EXTEND) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + { + int pushLength = 0; + int cx = x + Facing::STEP_X[facing]; + int cy = y + Facing::STEP_Y[facing]; + int cz = z + Facing::STEP_Z[facing]; + for (int i = 0; i < MAX_PUSH_DEPTH + 1; i++) + { + int block = level->getTile(cx, cy, cz); + if (block == 0) break; + if (!isPushable(block, level, cx, cy, cz, true)) break; + if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) { pushLength++; break; } + pushLength++; + if (i == MAX_PUSH_DEPTH) break; + cx += Facing::STEP_X[facing]; + cy += Facing::STEP_Y[facing]; + cz += Facing::STEP_Z[facing]; + } + if (FourKitBridge::FirePistonExtend(level->dimension->id, x, y, z, facing, pushLength)) + { + ignoreUpdate(false); + return false; + } + } +#endif PIXBeginNamedEvent(0,"Create push\n"); if (createPush(level, x, y, z, facing)) { @@ -256,6 +284,14 @@ bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, } else if (param1 == TRIGGER_CONTRACT) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FirePistonRetract(level->dimension->id, x, y, z, facing)) + { + level->setData(x, y, z, facing | EXTENDED_BIT, UPDATE_CLIENTS); + ignoreUpdate(false); + return false; + } +#endif PIXBeginNamedEvent(0,"Contract phase A\n"); shared_ptr prevTileEntity = level->getTileEntity(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); if (prevTileEntity != nullptr && dynamic_pointer_cast(prevTileEntity) != nullptr) diff --git a/Minecraft.World/ReedTile.cpp b/Minecraft.World/ReedTile.cpp index dc60bfba6..7a7266322 100644 --- a/Minecraft.World/ReedTile.cpp +++ b/Minecraft.World/ReedTile.cpp @@ -5,6 +5,10 @@ #include "net.minecraft.world.level.material.h" #include "net.minecraft.world.phys.h" #include "ReedTile.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif ReedTile::ReedTile(int id) : Tile( id, Material::plant,isSolidRender() ) { @@ -33,6 +37,10 @@ void ReedTile::tick(Level *level, int x, int y, int z, Random* random) int age = level->getData(x, y, z); if (age == 15) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockGrow(level->dimension->id, x, y + 1, z, id, 0)) + return; +#endif level->setTileAndUpdate(x, y + 1, z, id); level->setData(x, y, z, 0, Tile::UPDATE_NONE); } diff --git a/Minecraft.World/StemTile.cpp b/Minecraft.World/StemTile.cpp index 476b643cf..65d63631e 100644 --- a/Minecraft.World/StemTile.cpp +++ b/Minecraft.World/StemTile.cpp @@ -6,6 +6,10 @@ #include "..\Minecraft.Client\Minecraft.h" #include "..\Minecraft.Client\Common\Colours\ColourTable.h" #include "StemTile.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\FourKitBridge.h" +#include "Dimension.h" +#endif const wstring StemTile::TEXTURE_ANGLED = L"stem_bent"; @@ -60,6 +64,9 @@ void StemTile::tick(Level *level, int x, int y, int z, Random *random) int below = level->getTile(xx, y - 1, zz); if (level->getTile(xx, y, zz) == 0 && (below == Tile::farmland_Id || below == Tile::dirt_Id || below == Tile::grass_Id)) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (!FourKitBridge::FireBlockGrow(level->dimension->id, xx, y, zz, fruit->id, 0)) +#endif level->setTileAndUpdate(xx, y, zz, fruit->id); }