mirror of
https://codeberg.org/moonleay/Gimbal.git
synced 2024-11-27 14:52:08 +00:00
feat: added multiplayer support
parent
136bd78f00
commit
60ceb1b3ae
22 changed files with 183 additions and 42 deletions
|
@ -67,6 +67,8 @@ dependencies {
|
|||
modImplementation("net.fabricmc:fabric-language-kotlin:$fabricKotlinVersion")
|
||||
|
||||
modImplementation("de.huebcraft.mod-libs:configlib:$configlibVersion")
|
||||
|
||||
modImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
|
||||
}
|
||||
|
||||
val targetJavaVersion = 17
|
||||
|
|
|
@ -2,12 +2,18 @@ package net.moonleay.gimble
|
|||
|
||||
import net.moonleay.gimble.build.BuildConstants
|
||||
import net.fabricmc.api.ModInitializer
|
||||
import net.moonleay.gimble.networking.GimbleServer
|
||||
import org.apache.logging.log4j.LogManager
|
||||
|
||||
internal object Main : ModInitializer {
|
||||
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
|
||||
|
||||
override fun onInitialize() {
|
||||
LOGGER.info("Main has been initialized")
|
||||
LOGGER.info("Initializing Gimble on the common side...")
|
||||
LOGGER.info("Registering packets...")
|
||||
GimbleServer.registerPacketHandler()
|
||||
LOGGER.info("Packets have been registered.")
|
||||
LOGGER.info("Gimble has been initialized on the common side.")
|
||||
LOGGER.info("${BuildConstants.modName} (${BuildConstants.modId}) v.${BuildConstants.modVersion} by moonleay")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,11 @@ package net.moonleay.gimble.client.editor
|
|||
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.moonleay.gimble.client.util.ChatUtil
|
||||
import net.moonleay.gimble.client.editor.modes.Mode
|
||||
import net.moonleay.gimble.client.editor.modes.ModeModifier
|
||||
import net.moonleay.gimble.editor.ServerEditorManager
|
||||
import net.moonleay.gimble.editor.state.EditorState
|
||||
import net.moonleay.gimble.editor.state.mode.Mode
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier
|
||||
import net.moonleay.gimble.networking.GimbleClient
|
||||
|
||||
object ClientEditor {
|
||||
var CURRENT_MODE = Mode.NORMAL
|
||||
|
@ -11,6 +14,15 @@ object ClientEditor {
|
|||
private val TEMP_DISABLED_MODIFIERS = mutableListOf<ModeModifier>()
|
||||
|
||||
|
||||
/*
|
||||
* Send an updated player state to the server
|
||||
* */
|
||||
fun updateServerState() {
|
||||
val state = EditorState(CURRENT_MODE, CURRENT_MODE_MODIFIER)
|
||||
GimbleClient.sendEditorState(state)
|
||||
ServerEditorManager.updateEditorState(MinecraftClient.getInstance().player!!.uuid, state)
|
||||
}
|
||||
|
||||
fun toggleModifier(mod: ModeModifier) {
|
||||
if (CURRENT_MODE.incompatibleModifiers.contains(mod)){
|
||||
if (TEMP_DISABLED_MODIFIERS.contains(mod))
|
||||
|
@ -33,6 +45,9 @@ object ClientEditor {
|
|||
checkForIncompatibleModeModifiers()
|
||||
}
|
||||
|
||||
/**
|
||||
* This runs on Mode updated
|
||||
*/
|
||||
fun checkForIncompatibleModeModifiers() {
|
||||
if (TEMP_DISABLED_MODIFIERS.size > 0) {
|
||||
CURRENT_MODE_MODIFIER.addAll(
|
||||
|
@ -53,6 +68,9 @@ object ClientEditor {
|
|||
}
|
||||
}
|
||||
|
||||
// Update State
|
||||
this.updateServerState()
|
||||
|
||||
if(TEMP_DISABLED_MODIFIERS.isNotEmpty()) {
|
||||
ChatUtil.addToChatHistory("The following modifiers are not supported by this editor mode and are therefore currently disabled: " +
|
||||
getListAsString(TEMP_DISABLED_MODIFIERS), MinecraftClient.getInstance())
|
||||
|
|
|
@ -4,7 +4,7 @@ import net.minecraft.client.MinecraftClient
|
|||
import net.minecraft.client.option.KeyBinding
|
||||
import net.moonleay.gimble.client.editor.ClientEditor
|
||||
import net.moonleay.gimble.client.keybindings.impl.GimbleShortcut
|
||||
import net.moonleay.gimble.client.editor.modes.Mode
|
||||
import net.moonleay.gimble.editor.state.mode.Mode
|
||||
|
||||
|
||||
class EnableInsertModeShortcut(key: KeyBinding): GimbleShortcut(key) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import net.minecraft.client.MinecraftClient
|
|||
import net.minecraft.client.option.KeyBinding
|
||||
import net.moonleay.gimble.client.editor.ClientEditor
|
||||
import net.moonleay.gimble.client.keybindings.impl.GimbleShortcut
|
||||
import net.moonleay.gimble.client.editor.modes.Mode
|
||||
import net.moonleay.gimble.editor.state.mode.Mode
|
||||
|
||||
|
||||
class EnableReplaceModeShortcut(key: KeyBinding): GimbleShortcut(key) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import net.minecraft.client.MinecraftClient
|
|||
import net.minecraft.client.option.KeyBinding
|
||||
import net.moonleay.gimble.client.editor.ClientEditor
|
||||
import net.moonleay.gimble.client.keybindings.impl.GimbleShortcut
|
||||
import net.moonleay.gimble.client.editor.modes.Mode
|
||||
import net.moonleay.gimble.editor.state.mode.Mode
|
||||
|
||||
|
||||
class EnableVisualModeShortcut(key: KeyBinding): GimbleShortcut(key) {
|
||||
|
|
|
@ -4,12 +4,10 @@ import net.minecraft.client.MinecraftClient
|
|||
import net.minecraft.client.option.KeyBinding
|
||||
import net.moonleay.gimble.client.editor.ClientEditor
|
||||
import net.moonleay.gimble.client.keybindings.impl.GimbleShortcut
|
||||
import net.moonleay.gimble.client.editor.modes.ModeModifier
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier
|
||||
|
||||
class ToggleBulldozerModifierShortcut(key: KeyBinding): GimbleShortcut(key) {
|
||||
override fun onPressed(client: MinecraftClient) {
|
||||
ClientEditor.toggleModifier(ModeModifier.BULLDOZER)
|
||||
|
||||
ClientEditor.onModifiersUpdated()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,10 @@ import net.minecraft.client.MinecraftClient
|
|||
import net.minecraft.client.option.KeyBinding
|
||||
import net.moonleay.gimble.client.editor.ClientEditor
|
||||
import net.moonleay.gimble.client.keybindings.impl.GimbleShortcut
|
||||
import net.moonleay.gimble.client.editor.modes.ModeModifier
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier
|
||||
|
||||
class ToggleForcePlaceModifierShortcut(key: KeyBinding): GimbleShortcut(key) {
|
||||
override fun onPressed(client: MinecraftClient) {
|
||||
ClientEditor.toggleModifier(ModeModifier.FORCE_PLACE)
|
||||
|
||||
ClientEditor.onModifiersUpdated()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,10 @@ import net.minecraft.client.MinecraftClient
|
|||
import net.minecraft.client.option.KeyBinding
|
||||
import net.moonleay.gimble.client.editor.ClientEditor
|
||||
import net.moonleay.gimble.client.keybindings.impl.GimbleShortcut
|
||||
import net.moonleay.gimble.client.editor.modes.ModeModifier
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier
|
||||
|
||||
class ToggleNoClipModifierShortcut(key: KeyBinding): GimbleShortcut(key) {
|
||||
override fun onPressed(client: MinecraftClient) {
|
||||
ClientEditor.toggleModifier(ModeModifier.NO_CLIP)
|
||||
|
||||
ClientEditor.onModifiersUpdated()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,10 @@ import net.minecraft.client.MinecraftClient
|
|||
import net.minecraft.client.option.KeyBinding
|
||||
import net.moonleay.gimble.client.editor.ClientEditor
|
||||
import net.moonleay.gimble.client.keybindings.impl.GimbleShortcut
|
||||
import net.moonleay.gimble.client.editor.modes.ModeModifier
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier
|
||||
|
||||
class ToggleNoUpdatesModifierShortcut(key: KeyBinding): GimbleShortcut(key) {
|
||||
override fun onPressed(client: MinecraftClient) {
|
||||
ClientEditor.toggleModifier(ModeModifier.NO_UPDATES)
|
||||
|
||||
ClientEditor.onModifiersUpdated()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package net.moonleay.gimble.editor
|
||||
|
||||
import net.moonleay.gimble.build.BuildConstants
|
||||
import net.moonleay.gimble.editor.state.EditorState
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.util.UUID
|
||||
|
||||
object ServerEditorManager {
|
||||
private val LOGGER = LogManager.getLogger(BuildConstants.modName)
|
||||
|
||||
val STATEMAP = mutableMapOf<UUID, EditorState>()
|
||||
|
||||
fun updateEditorState(playerUUID: UUID, editorState: EditorState) {
|
||||
STATEMAP[playerUUID] = editorState
|
||||
LOGGER.info("$playerUUID: ${editorState.editorMode} with ${editorState.editorModifier}")
|
||||
}
|
||||
|
||||
fun getEditorState(playerUUID: UUID): EditorState? {
|
||||
return STATEMAP[playerUUID]
|
||||
}
|
||||
|
||||
fun playerHasModifier(playerUUID: UUID, modifier: ModeModifier): Boolean {
|
||||
if (!STATEMAP.containsKey(playerUUID))
|
||||
return false
|
||||
return STATEMAP[playerUUID]!!.editorModifier.contains(modifier) ?: false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package net.moonleay.gimble.editor.state
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.moonleay.gimble.editor.state.mode.Mode
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier
|
||||
|
||||
@Serializable
|
||||
data class EditorState(
|
||||
var editorMode: Mode,
|
||||
var editorModifier: MutableList<ModeModifier>,
|
||||
)
|
|
@ -1,6 +1,7 @@
|
|||
package net.moonleay.gimble.client.editor.modes
|
||||
package net.moonleay.gimble.editor.state.mode
|
||||
|
||||
enum class Mode(val displayName: String, val color: Int, val incompatibleModifiers: List<ModeModifier>){
|
||||
UNKNOWN("UNKNOWN", 0x000000, listOf()), // Unknown mode. This mode cannot be entered
|
||||
NORMAL("NORMAL", 0x90a959, listOf(ModeModifier.NO_UPDATES, ModeModifier.BULLDOZER, ModeModifier.FORCE_PLACE)), // Do nothing
|
||||
INSERT("INSERT", 0xf4bf75, listOf()), // Place and break blocks
|
||||
REPLACE("REPLACE", 0xac4242, listOf()), // Replace blocks
|
|
@ -1,4 +1,4 @@
|
|||
package net.moonleay.gimble.client.editor.modes
|
||||
package net.moonleay.gimble.editor.state.mode
|
||||
|
||||
enum class ModeModifier(val displayName: String) {
|
||||
// NONE("None"), // No Modifiers - default behavior
|
|
@ -6,8 +6,8 @@ import net.minecraft.client.gui.hud.InGameHud;
|
|||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import net.moonleay.gimble.client.editor.ClientEditor;
|
||||
import net.moonleay.gimble.client.editor.modes.Mode;
|
||||
import net.moonleay.gimble.client.editor.modes.ModeModifier;
|
||||
import net.moonleay.gimble.editor.state.mode.Mode;
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
|
@ -2,7 +2,7 @@ package net.moonleay.gimble.mixin;
|
|||
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.moonleay.gimble.client.editor.ClientEditor;
|
||||
import net.moonleay.gimble.client.editor.modes.ModeModifier;
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
|
|
|
@ -1,32 +1,59 @@
|
|||
package net.moonleay.gimble.mixin;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.entity.EntityPose;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerAbilities;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.moonleay.gimble.client.editor.ClientEditor;
|
||||
import net.moonleay.gimble.client.editor.modes.ModeModifier;
|
||||
import net.minecraft.world.World;
|
||||
import net.moonleay.gimble.editor.ServerEditorManager;
|
||||
import net.moonleay.gimble.editor.state.mode.ModeModifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public class NoClipMixin {
|
||||
import java.util.UUID;
|
||||
|
||||
@Shadow public boolean noClip;
|
||||
@Mixin(PlayerEntity.class)
|
||||
public abstract class NoClipMixin extends LivingEntity {
|
||||
@Shadow public abstract GameProfile getGameProfile();
|
||||
|
||||
@Inject(method = "tick", at = @At(value = "HEAD"))
|
||||
private void enoClip(CallbackInfo ci) {
|
||||
// TODO: Add player check, add multiplayer compat
|
||||
if(!((Entity)(Object)this instanceof PlayerEntity)) // Only check on player
|
||||
@Shadow public abstract PlayerAbilities getAbilities();
|
||||
|
||||
protected NoClipMixin(EntityType<? extends LivingEntity> entityType, World world) {
|
||||
super(entityType, world);
|
||||
} // Server side
|
||||
|
||||
|
||||
@Inject(method = "tick", at = @At(value = "FIELD",
|
||||
target = "Lnet/minecraft/entity/player/PlayerEntity;noClip:Z", shift = At.Shift.AFTER)
|
||||
) private void enoClip(CallbackInfo ci) {
|
||||
UUID uuid = this.getGameProfile().getId();
|
||||
|
||||
if (!ServerEditorManager.INSTANCE.playerHasModifier(uuid, ModeModifier.NO_CLIP)) {
|
||||
return; // NoClip is not enabled
|
||||
}
|
||||
if (!this.getAbilities().flying) {
|
||||
return;
|
||||
|
||||
if (!ClientEditor.INSTANCE.containsModifier(ModeModifier.NO_CLIP))
|
||||
return;
|
||||
PlayerEntity thePlayer = (PlayerEntity)(Object)this;
|
||||
if (!thePlayer.getAbilities().flying)
|
||||
return;
|
||||
|
||||
}
|
||||
// Enable NoClip
|
||||
this.noClip = true;
|
||||
}
|
||||
|
||||
@Inject(method = "updatePose", at = @At("HEAD"))
|
||||
private void onUpdatePose(CallbackInfo ci) {
|
||||
UUID uuid = this.getGameProfile().getId();
|
||||
|
||||
if (!ServerEditorManager.INSTANCE.playerHasModifier(uuid, ModeModifier.NO_CLIP))
|
||||
return; // NoClip is not enabled
|
||||
if (!this.getAbilities().flying)
|
||||
return;
|
||||
|
||||
// Force standing pose in NoClip mode
|
||||
|
||||
this.setPose(EntityPose.STANDING);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package net.moonleay.gimble.mixin;
|
|||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.option.GameOptions;
|
||||
import net.moonleay.gimble.client.editor.modes.Mode;
|
||||
import net.moonleay.gimble.editor.state.mode.Mode;
|
||||
import net.moonleay.gimble.client.editor.ClientEditor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
|
@ -12,7 +12,7 @@ import net.minecraft.util.hit.HitResult;
|
|||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.moonleay.gimble.client.editor.ClientEditor;
|
||||
import net.moonleay.gimble.client.editor.modes.Mode;
|
||||
import net.moonleay.gimble.editor.state.mode.Mode;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
21
src/main/java/net/moonleay/gimble/networking/GimbleClient.kt
Normal file
21
src/main/java/net/moonleay/gimble/networking/GimbleClient.kt
Normal file
|
@ -0,0 +1,21 @@
|
|||
package net.moonleay.gimble.networking
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.cbor.Cbor
|
||||
import kotlinx.serialization.encodeToByteArray
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||
import net.moonleay.gimble.editor.state.EditorState
|
||||
|
||||
object GimbleClient {
|
||||
|
||||
/**
|
||||
* Sends the given [EditorState] to the server.
|
||||
*/
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
fun sendEditorState(state: EditorState) {
|
||||
val buf = PacketByteBufs.create()
|
||||
buf.writeByteArray(Cbor.encodeToByteArray(state))
|
||||
ClientPlayNetworking.send(PacketIDs.UPDATE_EDITOR_STATE_ID, buf)
|
||||
}
|
||||
}
|
27
src/main/java/net/moonleay/gimble/networking/GimbleServer.kt
Normal file
27
src/main/java/net/moonleay/gimble/networking/GimbleServer.kt
Normal file
|
@ -0,0 +1,27 @@
|
|||
package net.moonleay.gimble.networking
|
||||
|
||||
import kotlinx.serialization.cbor.Cbor
|
||||
import kotlinx.serialization.decodeFromByteArray
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
import net.moonleay.gimble.editor.ServerEditorManager
|
||||
import net.moonleay.gimble.editor.state.EditorState
|
||||
|
||||
object GimbleServer {
|
||||
|
||||
fun registerPacketHandler() {
|
||||
ServerPlayNetworking
|
||||
.registerGlobalReceiver(PacketIDs.UPDATE_EDITOR_STATE_ID)
|
||||
{ server, player, handler, buf, responseSender ->
|
||||
handleStateUpdate(player, buf)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleStateUpdate(player: ServerPlayerEntity, buf: PacketByteBuf){
|
||||
val state = Cbor.decodeFromByteArray<EditorState>(buf.readByteArray())
|
||||
ServerEditorManager.updateEditorState(player.uuid, state)
|
||||
player.sendMessage(Text.of("Updated State (Server) ${state.editorMode} with ${state.editorModifier}"))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package net.moonleay.gimble.networking
|
||||
|
||||
import net.minecraft.util.Identifier
|
||||
import net.moonleay.gimble.build.BuildConstants
|
||||
|
||||
object PacketIDs {
|
||||
val UPDATE_EDITOR_STATE_ID = Identifier(BuildConstants.modId, "update_editor_state")
|
||||
}
|
Loading…
Reference in a new issue