Conversation
- Introduced `ServerMain.cpp` for the dedicated server logic, handling command-line arguments, server initialization, and network management. - Created `postbuild_server.ps1` script for post-build tasks, including copying necessary resources and DLLs for the dedicated server. - Added `CopyServerAssets.cmake` to manage the copying of server assets during the build process, ensuring required files are available for the dedicated server. - Defined project filters in `Minecraft.Server.vcxproj.filters` for better organization of server-related files.
- Introduced ServerLogger for logging startup steps and world I/O operations. - Implemented ServerProperties for loading and saving server configuration from `server.properties`. - Added WorldManager to handle world loading and creation based on server properties. - Updated ServerMain to integrate server properties loading and world management. - Enhanced project files to include new source and header files for the server components.
# Conflicts: # Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp # Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp # Minecraft.Client/Windows64/Network/WinsockNetLayer.h
Since 31881af56936aeef38ff322b975fd0 , `skinHud.swf` for 720 is not included in `MediaWindows64.arc`, the app crashes unless the virtual screen is set to HD.
…erver # Conflicts: # .gitignore # CMakeLists.txt # Minecraft.Client/Common/Network/GameNetworkManager.cpp
…ing sources - remove stale Windows64 Miles (mss64) link/copy references from server build - add Common/Filesystem/Filesystem.cpp to Minecraft.Server.vcxproj - add Windows64/PostProcesser.cpp to Minecraft.Server.vcxproj - fix unresolved externals (PostProcesser::*, FileExists) in dedicated server build
Since the crash caused by the 720p `skinHud.swf` not being included in `MediaWindows64.arc` has been resolved, switching back to 720p to reduce resource usage.
add with entrypoint and build scripts
…ft-LegacyConsoleEdition into feature/dedicated-server
on the server side, I fixed the behavior introduced after commit aadb511, where newly created worlds are intentionally not saved to disk immediately.
…in the classes to `server.properties`
LAN-Discovery, which isn’t needed in server mode and could potentially be a security risk, has also been disabled(only server mode).
- Integrated linenoise library for line editing and completion in the server console. - Updated ServerLogger to handle external writes safely during logging. - Modified ServerMain to initialize and manage the ServerCli for command input. - The implementation is separate from everything else, so it doesn't affect anything else. - The command input section and execution section are separated into threads.
Like most command line tools, it highlights predictions in gray.
Unified the scattered utility functions.
…down race Before this change, server/host shutdown closed sockets directly in ServerConnection::stop(), which bypassed the normal disconnect flow. As a result, clients could be dropped without receiving a proper DisconnectPacket during stop/kill/world-close paths. Also, WinsockNetLayer::Shutdown() could destroy synchronization objects while host-side recv threads were still exiting, causing a crash in RecvThreadProc (access violation on world close in host mode).
- Add client-side host disconnect handling in CPlatformNetworkManagerStub::DoWork() for _WINDOWS64. - When in QNET_STATE_GAME_PLAY as a non-host and WinsockNetLayer::IsConnected() becomes false, trigger g_NetworkManager.HandleDisconnect(false) to enter the normal disconnect/UI flow. - Use m_bLeaveGameOnTick as a one-shot guard to prevent repeated disconnect handling while the link remains down. - Reset m_bLeaveGameOnTick on LeaveGame(), HostGame(), and JoinGame() to avoid stale state across sessions.
Conflict resolution summary: - .gitignore: kept dedicated-server-specific ignore entries (tmp*/, _server_asset_probe/, server-data/) and also kept main-side entries (*.user, /out). - Minecraft.Client/Common/Network/GameNetworkManager.cpp: kept int64_t seed from main and preserved dedicatedNoLocalHostPlayer logic from dedicated-server. - Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp: preserved dedicated-server shutdown flow and also included main-side cleanup for s_smallIdToSocketLock.
Moved file operations to `utils`.
…P bans - add Access frontend that publishes thread-safe ban manager snapshots for dedicated server use - add BanManager storage for banned-players.json and banned-ips.json with load/save/update flows - add persistent player and IP ban checks during dedicated server connection handling - add UTF-8 BOM-safe JSON parsing and shared file helpers backed by nlohmann/json - add Unicode-safe ban file read/write and safer atomic replacement behavior on Windows - add active-ban snapshot APIs and expiry-aware filtering for expires metadata - add RAII-based dedicated access shutdown handling during server startup and teardown
- As a side effect, saving has become faster!
|
Why C#? |
|
@OpenSauce04 because i had tried lua before and it sucked and C# is good for beginners i think. and i can make it structured similarly to Bukkit as well |
|
Wouldn't C# give preference to windows? |
|
This is pretty cool, I have had many issues trying to add basic features to the legacy edition, so a C# plugin system would be much easier as it allows people who have experience in Unity to mod Minecraft LCE. |
C# is cross platform, so no. |
C# is performant, cross platform, has strong typing, and many other things and would probably be the best for this unironically. |
Yea but isn't it a lot easier to develop C# on windows, plus you don't need to install a huge runtime dependency since the runtime ships with the OS. The only time I've ever installed mono on Linux is for Terraria. Wouldn't something like WASM be better, esp since you can choose whatever language you want instead of being locked to something that a lot of people won't know. |
C# is not an unknown language, a lot of people know it, its used in Unity and a lot of other stuff, and is more beginner friendly than some other languages. Like i can just tell someone to go install .net sdk and visual studio or whatever they want to use for C# development that be vs on windows or whatever for linux and they are practically done with the dev tool setup all they gotta do is make the plugin itself. C# is also one of the first languages I learned outside of lua so i'd say its better for beginners. .NET is also not bundled with Windows or anything like that lol? its also a lot more mature compared to WASM, you arent locked to using windows for C# either, its made to be cross platform, the only restriction right now is the fact that the repo cannot be built natively to linux yet. |
|
WASM is pretty mature, about 10 years old, also a lot faster than C#. Whenever I see people talk about C# it's always people writing Windows first software that then gets ported to other platforms, I've never seen a Linux or macOS exclusive C# program. Kinda like how Swift is always macOS first even if it can technically run on anything. When I see something like this done by Linux people (and honestly even most Windows and mac people) it's always either Lua or WASM. Also doesn't Windows 11 bundle the .NET runtime? It's at least a common enough dependency that you can rely on almost all Windows users having it already, so defacto default. |
|
yk this was originally going to be lua but I wanted full parity with bukkit and somrthing easy for beginners, and also when I was trying lua it was very unstable, so I just ditched it and did c#, because ppl like loki and some others suggested it over lua and I lowkey didn't want to deal with lua anymore. This will have support for linux whenever the people make the main repo support linux fully. |
|
Yea lua sucks, I've that's why wasm is the clear choice. if for no other reason than speed. |
|
we will not be using lua or wasm, this is a C# plugin api and the language has already been agreed to be acceptable by the maintainers |
Lua has a better use for local modding of the game, like addon or datapack, while C# give a important role on server API. |
|
I don't think we're interested in lua anything officially just from talking with existing maintainers, bindings for a more useful language would make sense. Using Lua just perpetuates the "learn a useless programming language" thing Roblox has been doing to lock kids into their platform and that's not smthn we really want to be part of. |
That is not true. Lua is widely used for modding. Roblox is the tip top of popularity for this language, but years before this is the preferred language for Garry's Mod, MTA, and many others sandbox games/modloaders. |
|
I'd rather encourage people to learn a language that gives them a skill useful in the job market. |
|
I just want to add one more perspective in here: Using .NET for the API gives us a LOT more flexibility long term. Since we are using .NET, we aren't just limited to C#, the entire .NET ecosystem becomes available. This would include F#, VB, and other languages that compile to .NET IL. And not to mention, performance-wise, modern .NET is extremely fast and mature. .NET has JIT and AOT compilers, and from my experience, it is stable across platforms. WASM is cool, but it would introduce a second runtime, a sandbox model, and a lot of complexity for both maintainers and developers who would just want to create a mod or something. Most plugin developers already know C# from Unity or general .NET work, and C# is similar to Java in a sense, so it would be extremely easy for Java mod developers to come over to this, and .NET is strongly typed and would probably be much easier than forcing someone into WASM. |
This comment was marked as off-topic.
This comment was marked as off-topic.
|
Agreeing with C# here, very easy to learn and quite similar to java as an object oriented programming language |
|
id also like to see the Plugin API written straight in C# rather than in C++ just exposed on export, i think this would give us more flexiblity longterm and easier to document, the plugin API can still be loaded server side via interop then |
Description
This is a C# Plugin API for the dedicated server... It adds a api very similar to Bukkit.
There is a discord server where i post updates regarding this
Made to use .NET 8.0, will soon upgrade to .NET 10.0
I REAAALLLYY want more people to help work on this. This plans to be almost full parity with Bukkit.
I'll consider this ready to merge when I am able to port my 1.6 Bukkit plugin with no issues.
Changes
i have added plugin initialization under ServerMain.cpp, and a new project called "Minecraft.Server.FourKit"
This project is what links the C# and C++ together, and is also what plugin devs should include in their plugins.
Plugins devs can include either the full project or the DLL that gets built from Minecraft.Server.FourKit in their C# project.
Plugins go under the plugins folder and plugins are compiled as a DLL.
There is also nightly releases for this, and it should work with docker. Anyone updating to latest nightly should update both the DLL and the EXE.
The workflows have also been modified to account for .NET and other changes.
Many files (primarily PlayerConnection.cpp) have been modified to emit events to FourKit.
how to use!
creating le plugin
as stated above, plugin devs must include the DLL or the full "Minecraft.Server.FourKit" project
after that, there must be a file somewhere in the DLL that interfaces with ServerPlugin
registering a listener
Just like bukkit, you can register a listener with
FourKit.addListener(new ListenerClass());Listener classes must use the
Listenerinterface.Example:
events
To use an event in a listener, each event must have
[EventHandler]before the actual function.Currently available events:
PlayerJoinEventPlayerLeaveEventPlayerChatEventBlockBreakEventBlockPlaceEventPlayerMoveEventPlayerKickEventSignChangeEventPlayerInteractEventPlayerDeathEventPlayerDropItemEventEntityDamageEventEntityDamageByEntityEventInventoryOpenEventcompile
When you compile a plugin, take its DLL and put it into the plugins folder in the server. Its as simple as that. when u start the server it should just Work.
click to expand this, this is a very long pr message, below will go over usages of every event, enum, how to make commands, etc and an example plugin
event usage
PlayerJoinEvent and PlayerLeaveEvent
Both of these only come with event.getPlayer()
Functions under the Player class include:
getName()getHealth()isInsideVehicle()getFood()getFallDistance()getYRot()getXRot()getX()getY()getZ()getDimension()setFallDistance(float)setHealth(float)setFood(int)sendMessage(string message)kickPlayer()teleport(float x, float y, float z)isSneaking()isSprinting()setSprinting(bool)getAddress()returns aInetSocketAddress(just like java bukkit!!)getGamemode()returns a GameMode enumsetGameMode(GameMode mode)isSleeping()getItemInHand()returns ItemStack or nullsetItemInHand(ItemStack item)setAllowFlight(bool)getAllowFlight()getExhaustion()getSaturation()giveExp(int amount)giveExpLevels(int amount)getTotalExperience()isFlying()setExhaustion(float value)setFlying(boolean value)setExp(float exp)setLevel(int level)setSaturation(float value)setWalkSpeed(float value)getWalkSpeed()getInventory()returns PlayerInventory classopenInventory(Inventory inv)PlayerPortalEvent
Cancellable, these functions are available:
setFrom(x, y, z)setTo(x,y,z)getFrom()getTo()getCause()which returns a TeleportCause enumSignChangeEvent
getLine(int line)getLines()returns String[]getBlock()returns the block of the signsetLine(int line, string content)PlayerKickEvent
isCancelled()setCancelled(bool)getReason()returns a DisconnectReasonsetLeaveMessage(string)to set a custom leave msggetLeaveMessage()gets a custom leave message if anygetPlayer()PlayerKickEvent is fired before PlayerLeaveEvent
InventoryOpenEvent
funcs:
getPlayer()getInventory()returns Inventory by default, however can be any other type of InventoryisCancelled()setCancelled(bool)Example usage
PlayerInteractEvent
getAction()returns Action enumgetPlayer()getClickedBlock()returns BlockgetBlockFace()returns BlockFacehasItem()setCancelled()isCancelled()PlayerDeathEvent
getEntity()same as getPlayer() but bukkit doesnt have that for this event, it was getEntity for some reason lmaogetDeathMessage()setDeathMessage(string)setKeepLevel(bool)setKeepInventory(bool)setNewExp(int)setNewLevel(int)getNewExp(int)getNewLevel(int)getKeepLevel()getKeepInventory()PlayerChatEvent
This comes with event.getPlayer(), event.getMessage(), event.setCancelled(bool), and event.isCancelled()
Cancelling makes it not actually send the chat packet out, but still lets you process the chat event.
BlockBreakEvent and BlockPlaceEvent
Comes with event.getPlayer(), event.getBlock(), event.setCancelled(bool), and event.isCancelled()
Cancelling does what you think it does. It cancels it!!! Pretty simple.
event.getBlock() returns a Block class, which has these methods:
breakNaturally()getX()getY()getZ()getType()setType(int id)setData(int aux)getData()EntityDamageEvent and EntityDamageByEntityEvent
AS OF RIGHT NOW THESE EVENTS ONLY FIRE FOR A Player BEING DAMAGED
EntityDamageEvent funcs:
getDamage()gets raw damage (as double)getFinalDamage()gets damage after calculation as double (armor etc)getEntity()returns Entity (can also be Player etc) that was damagedgetCause()returns DamageCause EnumsetCancelled(bool)isCancelled()setDamage(double damage)EntityDamageByEntityEvent extends entitydamageevent, and has this additional func:
getDamager()EXAMPLE USAGE:
PlayerDropItemEvent
getItemDrop()returns Item classgetPlayer()isCancelled()setCancelled(bool)PlayerMoveEvent
Comes with event.getPlayer(), event.getTo(), event.getFrom() event.setCancelled(bool), and event.isCancelled()
Cancelling stops the player from moving.
getTo and getFrom return a Location class, which has these methods:
getX()getY()getZ()I will replace some other stuff previously mentioned to use this Location class sometime soon.
Command System
There is a command system, similar to bukkit. commands registered via this dont work in console yet tho
This will be improved as time goes on.
you can run
FourKit.getCommand("test").setExecutor(new CoolCommand());to point a command to an executorgetCommand("test") returns a
PluginCommandwhich has these funcs rn:setExecutor(CommandExecutor class)getExecutor()setUsage(string usage)setAliases(List<string> aliasessetDescription(string)getAliases()getDescription()getUsage()getName()custom command classes look like this:
just like in bukkit, you can check if the sender is a player. theres no way to check if a sender is Server yet (due to commands ran by server not being bound to this)
Classes
ItemStack
ItemStack can be created by these methods:
new ItemStack(int id, int amount)new ItemStack(int id, int amount, int aux)Item
getItemStack()returns ItemStacksetItemStack(ItemStack item)setPickupDelay(int ticks)getPickupDelay()Default is 40 ticksgetLocation()returns LocationLocation
Can be created by
new Location(x,y,z)World
you can get a world by a few methods
any event that returns a player, you can run
getWorld()on the playeryou can also get world by
FourKit.getWorld("world")orFourKit.getWorld(0)it accepts a dimension id or the world name-# (the world name isnt actually bound to anything, it just converts world to 0 etc)
within the World class, you can run these functions:
createExplosion(double x, double y, double z, float power)createExplosion(double x, double y, double z, float power, bool setFire)createExplosion(double x, double y, double z, float power, bool setFire, bool breakBlocks)createExplosion(Location loc, float power)createExplosion(Location loc, float power, bool setFire)createExplosion(Location loc, float power, bool setFire, bool breakBlocks)dropItem(Location location, ItemStack item)dropItemNaturally(Location location, ItemStack item)(drops an item at the specified Location with a random offset)getBlockAt(int x, int y, int z)getBlockAt(Location location)getFullTime()getHighestBlockAt(int x, int z)returns a BlockgetHighestBlockAt(Location location)getHighestBlockYAt(int x, int z)returns intgetHighestBlockYAt(Location location)getPlayers()returns ListgetSeed()getSpawnLocation()returns a LocationgetThunderDuration()returns intgetTime()hasStorm()isThundering()setFullTime(int64 time)setSpawnLocation(int x, int y, int z)setStorm(bool)setThunderDuration(int duration)setThundering(bool thundering)setTime(int64 time)setWeatherDuration(int duration)strikeLightning(double x, double y, double z)strikeLightning(Location loc)strikeLightningEffect(double x, double y, double z)strikeLightningEffect(Location loc)FourKit
FourKit.broadcastMessage(string message)sends a msg to all playersFourKit.addListener(Listener)adds a listener class, example:FourKit.addListener(new CoolListener())FourKit.getCommand(string cmd)returns a PluginCommand for the commandFourKit.getPlayer(string username)gets a PlayerFourKit.getWorld(string worldname)gets a world by name (converts stuff likeworldto its respective dimension)FourKit.getWorld(int dimension)gets a world by dimension idFourKit.createInventory(int size)FourKit.createInventory(int size, string title)FourKit.createInventory(InventoryType type)FourKit.createInventory(InventoryType type, string title)PluginCommand
setExecutor(CommandExecutor executor)sets executor of a command, example:getCommand("test").setExecutor(new CoolExecutorClass())getExecutor()returns the executor for the commandPlayerInventory and Inventory
Funcs available under
Inventory:getType()returns InventoryType enumgetSize()getItem(int slot)returns ItemStack or nullsetItem(int slot, ItemStack item)sets item in a slotgetContents()returns ItemStack[]setContents(ItemStack[] contents)firstEmpty()returns first empty slotfirst()returns the first slot in the inventory containing an ItemStack with the given stack, if not found it returns -1contains(ItemStack item)returns true if the inventory contains any ItemStacks matching the given ItemStackcontains(ItemStack item, int amount)returns true if the inventory contains at least the minimum amount specified of exactly matching ItemStackscontainsAtLeast(ItemStack item, int amount)returns true if the inventory contains ItemStacks matching the given ItemStack whose amounts sum to at least the minimum amount specifiedall(ItemStack item)returns a Dictionary<int, ItemStack>, finds all slots in the inventory containing any ItemStacks with the given ItemStackaddItem(ItemStack[] items)returns a Dictionary<int, ItemStack>, stores the given ItemStacks in the inventoryremoveItem(ItemStack[] items)returns a Dictionary<int, ItemStack>, removes the given ItemStacks from the inventory.getTitle()gets the custom title if setFuncs available under
PlayerInventory:setItemInHand(ItemStack item)getItemInHand()returns a ItemStack or nullgetHolder()returns PlayergetHelmet()returns ItemStack or nullsetHelmet(ItemStack item)getChestplate()returns ItemStack or nullsetChestplate(ItemStack item)getLeggings()returns ItemStack or nullsetLeggings(ItemStack item)getBoots()returns ItemStack or nullsetBoots(ItemStack item)getHeldItemSlot()setHeldItemSlot(int id)getArmorContents()returns ItemStack[]setArmorContents(ItemStack[] items)Example usage of the getArmorContents() and setArmorContents() func:
DoubleChestInventory (extends Inventory)
getLeftSide()returns ItemStack[]getRightSide()returns ItemStack[]CraftingInventory (extends Inventory)
getResult()returns ItemStackgetMatrix()returns ItemStack[]BrewerInventory (extends Inventory)
getIngredient()returns ItemStacksetIngredient(ItemStack item)sets ingredientBeaconInventory (extends Inventory)
getItem()returns ItemStacksetItem(ItemStack item)sets item powering beaconFurnaceInventory (extends Inventory)
getSmelting()returns ItemStacksetSmelting(ItemStack item)sets smelting itemgetFuel()returns ItemStacksetFuel(ItemStack item)sets fuelgetResult()returns itemStacksetResult(ItemStack item)sets the resultHorseInventory (extends Inventory)
getSaddle()returns ItemStacksetSaddle(ItemStack item)sets the saddlegetArmor()returns ItemStacksetArmor(ItemStack item)sets the armor for hte horses invEnums
GameMode
ADVENTURECREATIVESURVIVALDamageCause
BLOCK_EXPLOSIONCONTACTCUSTOMDROWNINGENTITY_ATTACKENTITY_EXPLOSIONFALLFALLING_BLOCKFIREFIRE_TICKLAVALIGHTNINGMAGICMELTINGPOISONPROJECTILESTARVATIONSUFFOCATIONSUICIDETHORNSVOID_DAMAGEWITHERInteractAction
RIGHT_CLICK_BLOCKLEFT_CLICK_BLOCKTeleportCause
END_PORTALENDER_PEARLNETHER_PORTALPLUGINUNKNOWNDisconnectReason
NONELOGIN_TOO_LONGILLEGAL_STANCEILLEGAL_POSITIONMOVED_TOO_QUICKLYNO_FLYINGKICKEDTIMED_OUTCONNECTION_OVERFLOWEND_OF_STREAMSERVER_FULLOUTDATED_SERVEROUTDATED_CLIENTUNEXPECTED_PACKETNO_MULTIPLAYER_PRIVILEGES_HOSTNO_MULTIPLAYER_PRIVILEGES_JOINNO_UGC_ALL_LOCALNO_UGC_SINGLE_LOCALCONTENT_RESTRICTED_ALL_LOCALCONTENT_RESTRICTED_SINGLE_LOCALNO_UGC_REMOTENO_FRIENDS_IN_GAMEBANNEDNOT_FRIENDS_WITH_HOSTNAT_MISMATCHInventoryType
ANVILBEACONBREWINGCHESTCRAFTINGCREATIVEDISPENSERDROPPERENCHANTINGENDER_CHESTFURNACEHOPPERHORSEMERCHANTPLAYERWORKBENCHDOUBLE_CHESTexample full plugin
in this example when a user joins, it sends them a welcome message.
a user can also use the commands
tadpole,idk,nomove,noplace(not prefixed by a slash)dont ask why i made
tadpolea thing loltadpolemakes it cancel all break events, and replaces the block broken with stone.idkplaces stone above the players headnomoveprevents the player from movingnoplaceprevents the player from placing