diff --git a/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java b/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java index 876ce0a7c58..fc787c07789 100644 --- a/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java +++ b/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java @@ -67,19 +67,13 @@ import org.spongepowered.common.event.manager.SpongeEventManager; import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.launch.Launch; +import org.spongepowered.common.util.JvmUtil; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.model.PluginContributor; -import java.lang.management.ManagementFactory; -import java.lang.reflect.Method; import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; import java.text.DecimalFormat; -import java.text.MessageFormat; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -88,8 +82,6 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; -import javax.management.MBeanServer; - public class SpongeCommand { protected static final String INDENT = " "; @@ -417,26 +409,13 @@ private Command.Parameterized chunksSubcommand() { } private @NonNull CommandResult heapSubcommandExecutor(final CommandContext context) { - final Path p = Path.of(".", "dumps", - "heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + "-server.hprof"); - context.sendMessage(Identity.nil(), Component.text("Writing JVM heap data")); - SpongeCommon.logger().info("Writing JVM heap data to: {}", p.toAbsolutePath()); - try { - Files.createDirectories(p.getParent()); - - final Class clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean"); - final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); - final Object hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", clazz); - final Method m = clazz.getMethod("dumpHeap", String.class, boolean.class); - m.invoke(hotspotMBean, p.toString(), true); - context.sendMessage(Identity.nil(), Component.text("Heap dump complete")); - SpongeCommon.logger().info("Heap dump complete"); - } catch (final Throwable t) { - context.sendMessage(Identity.nil(), Component.text("Failed to write heap dump. Check the console for more information.")); - SpongeCommon.logger().error(t); - SpongeCommon.logger().error(MessageFormat.format("Could not write heap to {0}: {1}", p, t.getMessage())); + context.sendMessage(Component.text("Writing JVM heap data")); + if (JvmUtil.dumpHeap()) { + context.sendMessage(Component.text("Heap dump complete")); + return CommandResult.success(); + } else { + return CommandResult.error(Component.text("Failed to write heap dump. Check the console for more information.")); } - return CommandResult.success(); } private @NonNull CommandResult pluginsListSubcommand(final CommandContext context) { diff --git a/src/main/java/org/spongepowered/common/inventory/fabric/UniversalFabric.java b/src/main/java/org/spongepowered/common/inventory/fabric/UniversalFabric.java index 494b9672002..fbd77f9e4ef 100644 --- a/src/main/java/org/spongepowered/common/inventory/fabric/UniversalFabric.java +++ b/src/main/java/org/spongepowered/common/inventory/fabric/UniversalFabric.java @@ -67,6 +67,7 @@ public interface UniversalFabric extends Fabric, InventoryBridge { @Override default void fabric$clear() { InventoryTranslators.getTranslator(this.getClass()).clear(this); + this.fabric$captureContainer(); } @Override default void fabric$markDirty() { diff --git a/src/main/java/org/spongepowered/common/util/JvmUtil.java b/src/main/java/org/spongepowered/common/util/JvmUtil.java new file mode 100644 index 00000000000..a589bdc12cf --- /dev/null +++ b/src/main/java/org/spongepowered/common/util/JvmUtil.java @@ -0,0 +1,61 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.util; + +import org.spongepowered.common.SpongeCommon; + +import java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import javax.management.MBeanServer; + +public final class JvmUtil { + + public static boolean dumpHeap() { + final Path p = Path.of(".", "dumps", + "heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + "-server.hprof"); + SpongeCommon.logger().info("Writing JVM heap data to: {}", p.toAbsolutePath()); + try { + Files.createDirectories(p.getParent()); + + final Class clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean"); + final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + final Object hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", clazz); + final Method m = clazz.getMethod("dumpHeap", String.class, boolean.class); + m.invoke(hotspotMBean, p.toString(), true); + SpongeCommon.logger().info("Heap dump complete"); + return true; + } catch (final Throwable t) { + SpongeCommon.logger().error(t); + SpongeCommon.logger().error(MessageFormat.format("Could not write heap to {0}: {1}", p, t.getMessage())); + return false; + } + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/ServerWatchdogMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/ServerWatchdogMixin.java new file mode 100644 index 00000000000..7f15cd285fb --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/dedicated/ServerWatchdogMixin.java @@ -0,0 +1,43 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.server.dedicated; + +import net.minecraft.server.dedicated.ServerWatchdog; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.util.JvmUtil; + +@Mixin(ServerWatchdog.class) +public abstract class ServerWatchdogMixin { + + @Inject(method = "exit", at = @At("HEAD")) + private void impl$dumpHeap(final CallbackInfo ci) { + if (Boolean.getBoolean("sponge.watchdogWriteDumpHeap")) { + JvmUtil.dumpHeap(); + } + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PrimaryLevelDataMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PrimaryLevelDataMixin.java index c9f868d6a67..bb381412a9b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PrimaryLevelDataMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/storage/PrimaryLevelDataMixin.java @@ -70,7 +70,6 @@ import org.spongepowered.common.world.server.SpongeWorldManager; import org.spongepowered.math.vector.Vector3i; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -101,8 +100,6 @@ public abstract class PrimaryLevelDataMixin implements WorldData, PrimaryLevelDa private InheritableConfigHandle impl$configAdapter; private final BiMap impl$playerUniqueIdMap = HashBiMap.create(); - private final List impl$pendingUniqueIds = new ArrayList<>(); - private int impl$trackedUniqueIdCount = 0; private boolean impl$customDifficulty = false, impl$customGameType = false, impl$customSpawnPosition = false, impl$loadOnStartup, impl$performsSpawnLogic; @@ -319,9 +316,9 @@ public abstract class PrimaryLevelDataMixin implements WorldData, PrimaryLevelDa return index; } - this.impl$playerUniqueIdMap.put(this.impl$trackedUniqueIdCount, uuid); - this.impl$pendingUniqueIds.add(uuid); - return this.impl$trackedUniqueIdCount++; + final int newIndex = this.impl$playerUniqueIdMap.size(); + this.impl$playerUniqueIdMap.put(newIndex, uuid); + return newIndex; } @Override @@ -378,12 +375,7 @@ public ServerLevelData overworldData() { // TODO Move this to Schema dynamic.get(Constants.Sponge.LEGACY_SPONGE_PLAYER_UUID_TABLE).readList(LegacyUUIDCodec.CODEC).result().orElseGet(() -> dynamic.get(Constants.Sponge.SPONGE_PLAYER_UUID_TABLE).readList(UUIDUtil.CODEC).result().orElse(Collections.emptyList()) - ).forEach(uuid -> { - final Integer playerIndex = this.impl$playerUniqueIdMap.inverse().get(uuid); - if (playerIndex == null) { - this.impl$playerUniqueIdMap.put(this.impl$trackedUniqueIdCount++, uuid); - } - }); + ).forEach(uuid -> this.impl$playerUniqueIdMap.inverse().putIfAbsent(uuid, this.impl$playerUniqueIdMap.size())); } @Override @@ -399,8 +391,6 @@ public ServerLevelData overworldData() { final ListTag playerIdList = new ListTag(); data.put(Constants.Sponge.SPONGE_PLAYER_UUID_TABLE, playerIdList); this.impl$playerUniqueIdMap.values().forEach(uuid -> playerIdList.add(new IntArrayTag(UUIDUtil.uuidToIntArray(uuid)))); - this.impl$pendingUniqueIds.forEach(uuid -> playerIdList.add(new IntArrayTag(UUIDUtil.uuidToIntArray(uuid)))); - this.impl$pendingUniqueIds.clear(); return data; } diff --git a/src/mixins/resources/mixins.sponge.core.json b/src/mixins/resources/mixins.sponge.core.json index b0947d8b34b..aa375f1ecc1 100644 --- a/src/mixins/resources/mixins.sponge.core.json +++ b/src/mixins/resources/mixins.sponge.core.json @@ -275,7 +275,8 @@ "server.network.MemoryServerHandshakePacketListenerImplMixin" ], "server": [ - "server.dedicated.DedicatedServerMixin" + "server.dedicated.DedicatedServerMixin", + "server.dedicated.ServerWatchdogMixin" ], "injectors": { "maxShiftBy": 3