9
0
mirror of https://github.com/Xiao-MoMi/Custom-Crops.git synced 2025-12-22 08:29:35 +00:00

region file

This commit is contained in:
XiaoMoMi
2024-03-18 05:05:56 +08:00
parent 0ce4d5a836
commit 68585d124a
26 changed files with 792 additions and 196 deletions

View File

@@ -133,7 +133,9 @@ public interface WorldManager extends Reloadable {
void handleChunkUnload(Chunk bukkitChunk);
void saveChunkToFile(CustomCropsChunk chunk);
void saveChunkToCachedRegion(CustomCropsChunk chunk);
void saveRegionToFile(CustomCropsRegion region);
void removeGlassAt(@NotNull SimpleLocation location);
@@ -142,4 +144,6 @@ public interface WorldManager extends Reloadable {
CustomCropsBlock removeAnythingAt(SimpleLocation location);
AbstractWorldAdaptor getWorldAdaptor();
void saveInfoData(CustomCropsWorld customCropsWorld);
}

View File

@@ -56,4 +56,6 @@ public interface Pot extends KeyItem {
String getBlockState(boolean water, FertilizerType type);
boolean isVanillaBlock();
boolean isWetPot(String id);
}

View File

@@ -19,12 +19,14 @@ package net.momirealms.customcrops.api.mechanic.world;
import net.momirealms.customcrops.api.manager.WorldManager;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsRegion;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import org.bukkit.event.Listener;
public abstract class AbstractWorldAdaptor implements Listener {
public static final int version = 1;
public static final int chunkVersion = 1;
public static final int regionVersion = 1;
protected WorldManager worldManager;
public AbstractWorldAdaptor(WorldManager worldManager) {
@@ -35,9 +37,13 @@ public abstract class AbstractWorldAdaptor implements Listener {
public abstract void init(CustomCropsWorld customCropsWorld);
public abstract void loadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate);
public abstract void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos);
public abstract void unloadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate);
public abstract void unloadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos);
public abstract void saveDynamicData(CustomCropsWorld ccWorld, CustomCropsChunk chunk);
public abstract void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk);
public abstract void saveRegion(CustomCropsRegion customCropsRegion);
public abstract void saveInfoData(CustomCropsWorld customCropsWorld);
}

View File

@@ -35,7 +35,7 @@ public class BlockPos {
return new BlockPos(location.getX() % 16, location.getY(), location.getZ() % 16);
}
public SimpleLocation getLocation(String world, ChunkCoordinate coordinate) {
public SimpleLocation getLocation(String world, ChunkPos coordinate) {
return new SimpleLocation(world, coordinate.x() * 16 + getX(), getY(), coordinate.z() * 16 + getZ());
}

View File

@@ -20,20 +20,18 @@ package net.momirealms.customcrops.api.mechanic.world;
import org.bukkit.Chunk;
import org.jetbrains.annotations.NotNull;
public record ChunkCoordinate(int x, int z) {
public record ChunkPos(int x, int z) {
private static final ChunkCoordinate empty = new ChunkCoordinate(0, 0);
public static ChunkCoordinate of(int x, int z) {
return new ChunkCoordinate(x, z);
public static ChunkPos of(int x, int z) {
return new ChunkPos(x, z);
}
public static ChunkCoordinate getByString(String coordinate) {
public static ChunkPos getByString(String coordinate) {
String[] split = coordinate.split(",", 2);
try {
int x = Integer.parseInt(split[0]);
int z = Integer.parseInt(split[1]);
return new ChunkCoordinate(x, z);
return new ChunkPos(x, z);
}
catch (NumberFormatException e) {
e.printStackTrace();
@@ -55,7 +53,7 @@ public record ChunkCoordinate(int x, int z) {
if (getClass() != obj.getClass()) {
return false;
}
final ChunkCoordinate other = (ChunkCoordinate) obj;
final ChunkPos other = (ChunkPos) obj;
if (this.x != other.x) {
return false;
}
@@ -66,13 +64,22 @@ public record ChunkCoordinate(int x, int z) {
}
@NotNull
public static ChunkCoordinate getByBukkitChunk(@NotNull Chunk chunk) {
return new ChunkCoordinate(chunk.getX(), chunk.getZ());
public RegionPos getRegionPos() {
return RegionPos.getByChunkPos(this);
}
@NotNull
public static ChunkPos getByBukkitChunk(@NotNull Chunk chunk) {
return new ChunkPos(chunk.getX(), chunk.getZ());
}
public String getAsString() {
return x + "," + z;
}
@Override
public String toString() {
return "ChunkCoordinate{" +
return "ChunkPos{" +
"x=" + x +
", z=" + z +
'}';

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.api.mechanic.world;
import org.bukkit.Chunk;
import org.jetbrains.annotations.NotNull;
public record RegionPos(int x, int z) {
public static RegionPos of(int x, int z) {
return new RegionPos(x, z);
}
public static RegionPos getByString(String coordinate) {
String[] split = coordinate.split(",", 2);
try {
int x = Integer.parseInt(split[0]);
int z = Integer.parseInt(split[1]);
return new RegionPos(x, z);
}
catch (NumberFormatException e) {
e.printStackTrace();
return null;
}
}
@Override
public int hashCode() {
long combined = (long) x << 32 | z;
return Long.hashCode(combined);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final RegionPos other = (RegionPos) obj;
if (this.x != other.x) {
return false;
}
if (this.z != other.z) {
return false;
}
return true;
}
@NotNull
public static RegionPos getByBukkitChunk(@NotNull Chunk chunk) {
int regionX = (int) Math.floor((double) chunk.getX() / 32.0);
int regionZ = (int) Math.floor((double) chunk.getZ() / 32.0);
return new RegionPos(regionX, regionZ);
}
@NotNull
public static RegionPos getByChunkPos(@NotNull ChunkPos chunk) {
int regionX = (int) Math.floor((double) chunk.x() / 32.0);
int regionZ = (int) Math.floor((double) chunk.z() / 32.0);
return new RegionPos(regionX, regionZ);
}
@Override
public String toString() {
return "RegionPos{" +
"x=" + x +
", z=" + z +
'}';
}
}

View File

@@ -57,8 +57,8 @@ public class SimpleLocation {
return worldName;
}
public ChunkCoordinate getChunkCoordinate() {
return new ChunkCoordinate(x >> 4, z >> 4);
public ChunkPos getChunkCoordinate() {
return new ChunkPos(x >> 4, z >> 4);
}
public SimpleLocation add(int x, int y, int z) {

View File

@@ -21,7 +21,7 @@ import net.momirealms.customcrops.api.mechanic.item.Crop;
import net.momirealms.customcrops.api.mechanic.item.Fertilizer;
import net.momirealms.customcrops.api.mechanic.item.Pot;
import net.momirealms.customcrops.api.mechanic.item.Sprinkler;
import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
@@ -33,7 +33,9 @@ public interface CustomCropsChunk {
CustomCropsWorld getCustomCropsWorld();
ChunkCoordinate getChunkCoordinate();
CustomCropsRegion getCustomCropsRegion();
ChunkPos getChunkPos();
void secondTimer();

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.api.mechanic.world.level;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.RegionPos;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public interface CustomCropsRegion {
CustomCropsWorld getCustomCropsWorld();
byte @Nullable [] getChunkBytes(ChunkPos pos);
RegionPos getRegionPos();
void removeChunk(ChunkPos pos);
void saveChunk(ChunkPos pos, byte[] data);
Map<ChunkPos, byte[]> getRegionDataToSave();
boolean canPrune();
}

View File

@@ -21,8 +21,9 @@ import net.momirealms.customcrops.api.mechanic.item.Crop;
import net.momirealms.customcrops.api.mechanic.item.Fertilizer;
import net.momirealms.customcrops.api.mechanic.item.Pot;
import net.momirealms.customcrops.api.mechanic.item.Sprinkler;
import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.RegionPos;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import org.bukkit.World;
@@ -33,11 +34,15 @@ import java.util.Optional;
public interface CustomCropsWorld {
void save();
void startTick();
void cancelTick();
CustomCropsChunk removeLazyChunkAt(ChunkCoordinate chunkCoordinate);
boolean isRegionLoaded(RegionPos regionPos);
CustomCropsChunk removeLazyChunkAt(ChunkPos chunkPos);
WorldSetting getWorldSetting();
@@ -49,13 +54,17 @@ public interface CustomCropsWorld {
String getWorldName();
boolean isChunkLoaded(ChunkCoordinate chunkCoordinate);
boolean isChunkLoaded(ChunkPos chunkPos);
Optional<CustomCropsChunk> getChunkAt(ChunkCoordinate chunkCoordinate);
Optional<CustomCropsChunk> getLoadedChunkAt(ChunkPos chunkPos);
Optional<CustomCropsRegion> getLoadedRegionAt(RegionPos regionPos);
void loadRegion(CustomCropsRegion region);
void loadChunk(CustomCropsChunk chunk);
void unloadChunk(ChunkCoordinate chunkCoordinate);
void unloadChunk(ChunkPos chunkPos);
void setInfoData(WorldInfoData infoData);

View File

@@ -8,7 +8,7 @@ plugins {
allprojects {
project.group = "net.momirealms"
project.version = "3.4.2.3"
project.version = "3.4.3.0"
apply<JavaPlugin>()
apply(plugin = "java")

View File

@@ -103,7 +103,6 @@ public class CustomCropsPluginImpl extends CustomCropsPlugin {
this.disableNBTAPILogs();
Migration.tryUpdating();
this.reload();
this.worldManager.init();
if (ConfigManager.metrics()) new Metrics(this, 16593);
if (ConfigManager.checkUpdate()) {
this.versionManager.checkUpdate().thenAccept(result -> {

View File

@@ -21,7 +21,6 @@ import dev.jorel.commandapi.*;
import dev.jorel.commandapi.arguments.ArgumentSuggestions;
import dev.jorel.commandapi.arguments.IntegerArgument;
import dev.jorel.commandapi.arguments.StringArgument;
import dev.jorel.commandapi.arguments.WorldArgument;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.common.Initable;
import net.momirealms.customcrops.api.integration.SeasonInterface;
@@ -33,7 +32,9 @@ import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsSection;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import net.momirealms.customcrops.compatibility.season.InBuiltSeason;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.generator.WorldInfo;
import java.util.Locale;
import java.util.Optional;
@@ -91,10 +92,15 @@ public class CommandManager implements Initable {
private CommandAPICommand getForceTickCommand() {
return new CommandAPICommand("force-tick")
.withArguments(new WorldArgument("world"))
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> Bukkit.getWorlds().stream().map(WorldInfo::getName).toList().toArray(new String[0]))))
.withArguments(new StringArgument("type").replaceSuggestions(ArgumentSuggestions.strings("sprinkler", "crop", "pot", "scarecrow", "greenhouse")))
.executes((sender, args) -> {
World world = (World) args.get("world");
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
ItemType itemType = ItemType.valueOf(((String) args.get("type")).toUpperCase(Locale.ENGLISH));
Optional<CustomCropsWorld> customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world);
if (customCropsWorld.isEmpty()) {
@@ -119,16 +125,26 @@ public class CommandManager implements Initable {
return new CommandAPICommand("date")
.withSubcommands(
new CommandAPICommand("get")
.withArguments(new WorldArgument("world"))
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> Bukkit.getWorlds().stream().map(WorldInfo::getName).toList().toArray(new String[0]))))
.executes((sender, args) -> {
World world = (World) args.get("world");
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
plugin.getAdventure().sendMessageWithPrefix(sender, String.valueOf(plugin.getIntegrationManager().getDate(world)));
}),
new CommandAPICommand("set")
.withArguments(new WorldArgument("world"))
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> Bukkit.getWorlds().stream().map(WorldInfo::getName).toList().toArray(new String[0]))))
.withArguments(new IntegerArgument("date",1))
.executes((sender, args) -> {
World world = (World) args.get("world");
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
int date = (int) args.getOrDefault("date", 1);
SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface();
if (!(seasonInterface instanceof InBuiltSeason inBuiltSeason)) {
@@ -159,13 +175,18 @@ public class CommandManager implements Initable {
return new CommandAPICommand("season")
.withSubcommands(
new CommandAPICommand("get")
.withArguments(new WorldArgument("world"))
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> Bukkit.getWorlds().stream().map(WorldInfo::getName).toList().toArray(new String[0]))))
.executes((sender, args) -> {
World world = (World) args.get("world");
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeason(world)));
}),
new CommandAPICommand("set")
.withArguments(new WorldArgument("world"))
.withArguments(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> Bukkit.getWorlds().stream().map(WorldInfo::getName).toList().toArray(new String[0]))))
.withArguments(new StringArgument("season")
.replaceSuggestions(ArgumentSuggestions.stringsWithTooltips(info ->
new IStringTooltip[] {
@@ -177,7 +198,12 @@ public class CommandManager implements Initable {
))
)
.executes((sender, args) -> {
World world = (World) args.get("world");
String worldName = (String) args.get("world");
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world");
return;
}
String seasonName = (String) args.get("season");
SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface();

View File

@@ -41,7 +41,7 @@ import net.momirealms.customcrops.api.mechanic.misc.CRotation;
import net.momirealms.customcrops.api.mechanic.misc.Reason;
import net.momirealms.customcrops.api.mechanic.misc.Value;
import net.momirealms.customcrops.api.mechanic.requirement.Requirement;
import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop;
@@ -443,7 +443,7 @@ public class ActionManagerImpl implements ActionManager {
if (Math.random() > chance) return;
Location location = state.getLocation();
plugin.getWorldManager().getCustomCropsWorld(location.getWorld())
.flatMap(world -> world.getChunkAt(ChunkCoordinate.getByBukkitChunk(location.getChunk())))
.flatMap(world -> world.getLoadedChunkAt(ChunkPos.getByBukkitChunk(location.getChunk())))
.flatMap(chunk -> chunk.getBlockAt(SimpleLocation.of(location)))
.ifPresent(block -> {
block.tick(1);

View File

@@ -2020,6 +2020,9 @@ public class ItemManagerImpl implements ItemManager {
if (optionalPot.isEmpty()) {
plugin.debug("Found a pot without data interacted by " + player.getName() + " at " + location);
WorldPot newPot = new MemoryPot(simpleLocation, pot.getKey());
if (pot.isWetPot(potItemID)) {
newPot.setWater(1);
}
plugin.getWorldManager().addPotAt(newPot, simpleLocation);
optionalPot = Optional.of(newPot);
} else {

View File

@@ -45,6 +45,7 @@ import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemDamageEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;

View File

@@ -161,4 +161,17 @@ public class PotConfig extends AbstractEventItem implements Pot {
public boolean isVanillaBlock() {
return isVanillaBlock;
}
@Override
public boolean isWetPot(String id) {
if (id.equals(getWetItem())) {
return true;
}
for (Pair<String, String> pair : fertilizedPotMap.values()) {
if (pair.right().equals(id)) {
return true;
}
}
return false;
}
}

View File

@@ -34,10 +34,26 @@ public class Migration {
if (version == null) return;
int versionNumber = Integer.parseInt(version);
if (!(versionNumber >= 25 && versionNumber <= 34)) {
if (versionNumber >= 25 && versionNumber <= 34) {
doV33Migration(config);
return;
}
if (versionNumber == 35) {
doV343Migration();
}
}
private static void doV343Migration() {
if (CustomCropsPlugin.get().getWorldManager().getWorldAdaptor() instanceof BukkitWorldAdaptor adaptor) {
for (World world : Bukkit.getWorlds()) {
CWorld temp = new CWorld(CustomCropsPlugin.getInstance().getWorldManager(), world);
temp.setWorldSetting(WorldSetting.of(false,300,true, 1,true,2,true,2,false,false,false,28,-1,-1,-1, 0));
adaptor.convertWorldFromV342toV343(temp, world);
}
}
}
private static void doV33Migration(YamlConfiguration config) {
// do migration
if (config.contains("mechanics.season.sync-season")) {
config.set("mechanics.sync-season.enable", config.getBoolean("mechanics.season.sync-season.enable"));
@@ -53,7 +69,7 @@ public class Migration {
}
try {
config.save(configFile);
config.save(new File(CustomCropsPlugin.getInstance().getDataFolder(), "config.yml"));
} catch (IOException e) {
e.printStackTrace();
}
@@ -87,7 +103,7 @@ public class Migration {
for (World world : Bukkit.getWorlds()) {
CWorld temp = new CWorld(CustomCropsPlugin.getInstance().getWorldManager(), world);
temp.setWorldSetting(WorldSetting.of(false,300,true, 1,true,2,true,2,false,false,false,28,-1,-1,-1, 0));
adaptor.convertWorld(temp, world);
adaptor.convertWorldFromV33toV34(temp, world);
}
}
}

View File

@@ -25,8 +25,8 @@ import net.momirealms.customcrops.api.mechanic.item.Pot;
import net.momirealms.customcrops.api.mechanic.item.Sprinkler;
import net.momirealms.customcrops.api.mechanic.misc.CRotation;
import net.momirealms.customcrops.api.mechanic.requirement.State;
import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate;
import net.momirealms.customcrops.api.mechanic.world.BlockPos;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.level.*;
@@ -44,7 +44,7 @@ import java.util.concurrent.ThreadLocalRandom;
public class CChunk implements CustomCropsChunk {
private transient CWorld cWorld;
private final ChunkCoordinate chunkCoordinate;
private final ChunkPos chunkPos;
private final ConcurrentHashMap<Integer, CSection> loadedSections;
private final PriorityQueue<TickTask> queue;
private final Set<BlockPos> tickedBlocks;
@@ -52,9 +52,9 @@ public class CChunk implements CustomCropsChunk {
private int loadedSeconds;
private int unloadedSeconds;
public CChunk(CWorld cWorld, ChunkCoordinate chunkCoordinate) {
public CChunk(CWorld cWorld, ChunkPos chunkPos) {
this.cWorld = cWorld;
this.chunkCoordinate = chunkCoordinate;
this.chunkPos = chunkPos;
this.loadedSections = new ConcurrentHashMap<>(64);
this.queue = new PriorityQueue<>();
this.unloadedSeconds = 0;
@@ -64,7 +64,7 @@ public class CChunk implements CustomCropsChunk {
public CChunk(
CWorld cWorld,
ChunkCoordinate chunkCoordinate,
ChunkPos chunkPos,
int loadedSeconds,
long lastLoadedTime,
ConcurrentHashMap<Integer, CSection> loadedSections,
@@ -72,7 +72,7 @@ public class CChunk implements CustomCropsChunk {
HashSet<BlockPos> tickedBlocks
) {
this.cWorld = cWorld;
this.chunkCoordinate = chunkCoordinate;
this.chunkPos = chunkPos;
this.loadedSections = loadedSections;
this.lastLoadedTime = lastLoadedTime;
this.loadedSeconds = loadedSeconds;
@@ -101,8 +101,13 @@ public class CChunk implements CustomCropsChunk {
}
@Override
public ChunkCoordinate getChunkCoordinate() {
return chunkCoordinate;
public CustomCropsRegion getCustomCropsRegion() {
return cWorld.getLoadedRegionAt(chunkPos.getRegionPos()).orElse(null);
}
@Override
public ChunkPos getChunkPos() {
return chunkPos;
}
@Override

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customcrops.mechanic.world;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.RegionPos;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsRegion;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CRegion implements CustomCropsRegion {
private final CWorld cWorld;
private final RegionPos regionPos;
private final ConcurrentHashMap<ChunkPos, byte[]> cachedChunkBytes;
public CRegion(CWorld cWorld, RegionPos regionPos) {
this.cWorld = cWorld;
this.regionPos = regionPos;
this.cachedChunkBytes = new ConcurrentHashMap<>();
}
public CRegion(CWorld cWorld, RegionPos regionPos, ConcurrentHashMap<ChunkPos, byte[]> cachedChunkBytes) {
this.cWorld = cWorld;
this.regionPos = regionPos;
this.cachedChunkBytes = cachedChunkBytes;
}
@Override
public CustomCropsWorld getCustomCropsWorld() {
return cWorld;
}
@Nullable
@Override
public byte[] getChunkBytes(ChunkPos pos) {
return cachedChunkBytes.get(pos);
}
@Override
public RegionPos getRegionPos() {
return regionPos;
}
@Override
public void removeChunk(ChunkPos pos) {
cachedChunkBytes.remove(pos);
}
@Override
public void saveChunk(ChunkPos pos, byte[] data) {
cachedChunkBytes.put(pos, data);
}
@Override
public Map<ChunkPos, byte[]> getRegionDataToSave() {
return new HashMap<>(cachedChunkBytes);
}
@Override
public boolean canPrune() {
return cachedChunkBytes.size() == 0;
}
}

View File

@@ -26,20 +26,23 @@ import net.momirealms.customcrops.api.mechanic.item.Crop;
import net.momirealms.customcrops.api.mechanic.item.Fertilizer;
import net.momirealms.customcrops.api.mechanic.item.Pot;
import net.momirealms.customcrops.api.mechanic.item.Sprinkler;
import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.RegionPos;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.level.*;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
import net.momirealms.customcrops.api.scheduler.CancellableTask;
import net.momirealms.customcrops.api.util.LogUtils;
import net.momirealms.customcrops.utils.EventUtils;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -47,21 +50,42 @@ public class CWorld implements CustomCropsWorld {
private final WorldManager worldManager;
private final WeakReference<World> world;
private final ConcurrentHashMap<ChunkCoordinate, CChunk> loadedChunks;
private final ConcurrentHashMap<ChunkCoordinate, CChunk> lazyChunks;
private final ConcurrentHashMap<ChunkPos, CChunk> loadedChunks;
private final ConcurrentHashMap<ChunkPos, CChunk> lazyChunks;
private final ConcurrentHashMap<RegionPos, CRegion> loadedRegions;
private WorldSetting setting;
private WorldInfoData infoData;
private final String worldName;
private CancellableTask worldTask;
private int currentMinecraftDay;
private int regionTimer;
public CWorld(WorldManager worldManager, World world) {
this.world = new WeakReference<>(world);
this.worldManager = worldManager;
this.loadedChunks = new ConcurrentHashMap<>();
this.lazyChunks = new ConcurrentHashMap<>();
this.loadedRegions = new ConcurrentHashMap<>();
this.worldName = world.getName();
this.currentMinecraftDay = (int) (world.getFullTime() / 24000);
this.regionTimer = 0;
}
@Override
public void save() {
long time1 = System.currentTimeMillis();
worldManager.saveInfoData(this);
for (CChunk chunk : loadedChunks.values()) {
worldManager.saveChunkToCachedRegion(chunk);
}
for (CChunk chunk : lazyChunks.values()) {
worldManager.saveChunkToCachedRegion(chunk);
}
for (CRegion region : loadedRegions.values()) {
worldManager.saveRegionToFile(region);
}
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to save world " + worldName + ". Saved " + (lazyChunks.size() + loadedChunks.size()) + " chunks.");
}
@Override
@@ -77,8 +101,8 @@ public class CWorld implements CustomCropsWorld {
}
private void timer() {
ArrayList<Pair<ChunkCoordinate, CChunk>> chunksToSave = new ArrayList<>();
for (Map.Entry<ChunkCoordinate, CChunk> lazyEntry : lazyChunks.entrySet()) {
ArrayList<Pair<ChunkPos, CChunk>> chunksToSave = new ArrayList<>();
for (Map.Entry<ChunkPos, CChunk> lazyEntry : lazyChunks.entrySet()) {
CChunk chunk = lazyEntry.getValue();
int sec = chunk.getUnloadedSeconds() + 1;
if (sec >= 30) {
@@ -87,9 +111,9 @@ public class CWorld implements CustomCropsWorld {
chunk.setUnloadedSeconds(sec);
}
}
for (Pair<ChunkCoordinate, CChunk> pair : chunksToSave) {
for (Pair<ChunkPos, CChunk> pair : chunksToSave) {
lazyChunks.remove(pair.left());
worldManager.saveChunkToFile(pair.right());
worldManager.saveChunkToCachedRegion(pair.right());
}
if (setting.isAutoSeasonChange()) {
this.updateSeasonAndDate();
@@ -99,6 +123,22 @@ public class CWorld implements CustomCropsWorld {
chunk.secondTimer();
}
}
// timer check to unload regions
this.regionTimer++;
if (this.regionTimer >= 600) {
this.regionTimer = 0;
ArrayList<RegionPos> removed = new ArrayList<>();
for (Map.Entry<RegionPos, CRegion> entry : loadedRegions.entrySet()) {
if (isRegionNoLongerLoaded(entry.getKey())) {
worldManager.saveRegionToFile(entry.getValue());
removed.add(entry.getKey());
}
}
for (RegionPos pos : removed) {
loadedRegions.remove(pos);
}
}
}
private void updateSeasonAndDate() {
@@ -130,8 +170,8 @@ public class CWorld implements CustomCropsWorld {
}
@Override
public CustomCropsChunk removeLazyChunkAt(ChunkCoordinate chunkCoordinate) {
return lazyChunks.remove(chunkCoordinate);
public CustomCropsChunk removeLazyChunkAt(ChunkPos chunkPos) {
return lazyChunks.remove(chunkPos);
}
@Override
@@ -161,31 +201,51 @@ public class CWorld implements CustomCropsWorld {
}
@Override
public boolean isChunkLoaded(ChunkCoordinate chunkCoordinate) {
return loadedChunks.containsKey(chunkCoordinate);
public boolean isChunkLoaded(ChunkPos chunkPos) {
return loadedChunks.containsKey(chunkPos);
}
@Override
public Optional<CustomCropsChunk> getChunkAt(ChunkCoordinate chunkCoordinate) {
return Optional.ofNullable(createOrGetChunk(chunkCoordinate));
public boolean isRegionLoaded(RegionPos regionPos) {
return loadedRegions.containsKey(regionPos);
}
@Override
public Optional<CustomCropsChunk> getLoadedChunkAt(ChunkPos chunkPos) {
return Optional.ofNullable(createOrGetChunk(chunkPos));
}
@Override
public Optional<CustomCropsRegion> getLoadedRegionAt(RegionPos regionPos) {
return Optional.ofNullable(loadedRegions.get(regionPos));
}
@Override
public void loadRegion(CustomCropsRegion region) {
RegionPos regionPos = region.getRegionPos();
if (loadedRegions.containsKey(regionPos)) {
LogUtils.warn("Invalid operation: Loaded region is loaded again." + regionPos);
return;
}
loadedRegions.put(regionPos, (CRegion) region);
}
@Override
public void loadChunk(CustomCropsChunk chunk) {
ChunkCoordinate chunkCoordinate = chunk.getChunkCoordinate();
if (loadedChunks.containsKey(chunkCoordinate)) {
LogUtils.warn("Invalid operation: Loaded chunk is loaded again." + chunkCoordinate);
ChunkPos chunkPos = chunk.getChunkPos();
if (loadedChunks.containsKey(chunkPos)) {
LogUtils.warn("Invalid operation: Loaded chunk is loaded again." + chunkPos);
return;
}
loadedChunks.put(chunkCoordinate, (CChunk) chunk);
loadedChunks.put(chunkPos, (CChunk) chunk);
}
@Override
public void unloadChunk(ChunkCoordinate chunkCoordinate) {
CChunk chunk = loadedChunks.remove(chunkCoordinate);
public void unloadChunk(ChunkPos chunkPos) {
CChunk chunk = loadedChunks.remove(chunkPos);
if (chunk != null) {
chunk.updateLastLoadedTime();
lazyChunks.put(chunkCoordinate, chunk);
lazyChunks.put(chunkPos, chunk);
}
}
@@ -256,7 +316,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addWaterToSprinkler(sprinkler, location, amount);
} else {
@@ -266,7 +326,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addFertilizerToPot(pot, fertilizer, location);
} else {
@@ -276,7 +336,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addWaterToPot(Pot pot, SimpleLocation location, int amount) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addWaterToPot(pot, location, amount);
} else {
@@ -286,7 +346,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addPotAt(WorldPot pot, SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addPotAt(pot, location);
} else {
@@ -296,7 +356,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addSprinklerAt(sprinkler, location);
} else {
@@ -306,7 +366,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addCropAt(WorldCrop crop, SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addCropAt(crop, location);
} else {
@@ -316,7 +376,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addPointToCrop(Crop crop, SimpleLocation location, int points) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addPointToCrop(crop, location, points);
} else {
@@ -326,7 +386,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addGlassAt(WorldGlass glass, SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addGlassAt(glass, location);
} else {
@@ -336,7 +396,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().addScarecrowAt(scarecrow, location);
} else {
@@ -346,7 +406,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void removeSprinklerAt(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().removeSprinklerAt(location);
} else {
@@ -356,7 +416,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void removePotAt(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().removePotAt(location);
} else {
@@ -366,7 +426,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void removeCropAt(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().removeCropAt(location);
} else {
@@ -376,7 +436,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void removeGlassAt(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().removeGlassAt(location);
} else {
@@ -386,7 +446,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public void removeScarecrowAt(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
chunk.get().removeScarecrowAt(location);
} else {
@@ -396,7 +456,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public CustomCropsBlock removeAnythingAt(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isPresent()) {
return chunk.get().removeBlockAt(location);
} else {
@@ -406,33 +466,35 @@ public class CWorld implements CustomCropsWorld {
}
@Nullable
private CustomCropsChunk createOrGetChunk(ChunkCoordinate chunkCoordinate) {
private CustomCropsChunk createOrGetChunk(ChunkPos chunkPos) {
World bukkitWorld = world.get();
if (bukkitWorld == null)
return null;
CChunk chunk = loadedChunks.get(chunkCoordinate);
CChunk chunk = loadedChunks.get(chunkPos);
if (chunk != null) {
return chunk;
}
if (bukkitWorld.isChunkLoaded(chunkCoordinate.x(), chunkCoordinate.z())) {
chunk = new CChunk(this, chunkCoordinate);
// is a loaded chunk, but it doesn't have customcrops data
if (bukkitWorld.isChunkLoaded(chunkPos.x(), chunkPos.z())) {
chunk = new CChunk(this, chunkPos);
loadChunk(chunk);
return chunk;
} else {
if (bukkitWorld.isChunkGenerated(chunkCoordinate.x(), chunkCoordinate.z())) {
Chunk bukkitChunk = bukkitWorld.getChunkAt(chunkCoordinate.x(), chunkCoordinate.z());
worldManager.handleChunkLoad(bukkitChunk);
chunk = loadedChunks.get(chunkCoordinate);
return Objects.requireNonNullElseGet(chunk, () -> new CChunk(this, chunkCoordinate));
} else {
return null;
}
// is an unloaded chunk, but has been generated
// if (bukkitWorld.isChunkGenerated(chunkPos.x(), chunkPos.z())) {
// Chunk bukkitChunk = bukkitWorld.getChunkAt(chunkPos.x(), chunkPos.z());
// worldManager.handleChunkLoad(bukkitChunk);
// chunk = loadedChunks.get(chunkPos);
// return Objects.requireNonNullElseGet(chunk, () -> new CChunk(this, chunkPos));
// } else {
return null;
// }
}
}
@Override
public boolean isPotReachLimit(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isEmpty()) {
LogUtils.warn("Invalid operation: Querying pot amount from a not generated chunk");
return true;
@@ -443,7 +505,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public boolean isCropReachLimit(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isEmpty()) {
LogUtils.warn("Invalid operation: Querying crop amount from a not generated chunk");
return true;
@@ -454,7 +516,7 @@ public class CWorld implements CustomCropsWorld {
@Override
public boolean isSprinklerReachLimit(SimpleLocation location) {
Optional<CustomCropsChunk> chunk = getChunkAt(location.getChunkCoordinate());
Optional<CustomCropsChunk> chunk = getLoadedChunkAt(location.getChunkCoordinate());
if (chunk.isEmpty()) {
LogUtils.warn("Invalid operation: Querying sprinkler amount from a not generated chunk");
return true;
@@ -463,10 +525,18 @@ public class CWorld implements CustomCropsWorld {
return chunk.get().getSprinklerAmount() >= setting.getSprinklerPerChunk();
}
public Collection<CChunk> getAllChunksToSave() {
ArrayList<CChunk> chunks = new ArrayList<>();
chunks.addAll(lazyChunks.values());
chunks.addAll(loadedChunks.values());
return chunks;
private boolean isRegionNoLongerLoaded(RegionPos region) {
World w = world.get();
if (w != null) {
for (int chunkX = region.x() * 32; chunkX < region.x() * 32 + 32; chunkX++) {
for (int chunkZ = region.z() * 32; chunkZ < region.z() * 32 + 32; chunkZ++) {
// if a chunk is unloaded, then it should not be in the loaded chunks map
if (w.isChunkLoaded(chunkX, chunkZ) || lazyChunks.containsKey(ChunkPos.of(chunkX, chunkZ))) {
return false;
}
}
}
}
return true;
}
}

View File

@@ -66,4 +66,8 @@ public class SerializableChunk {
public int[] getTicked() {
return ticked;
}
public boolean canPrune() {
return sections.size() == 0;
}
}

View File

@@ -21,7 +21,7 @@ import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.manager.WorldManager;
import net.momirealms.customcrops.api.mechanic.item.*;
import net.momirealms.customcrops.api.mechanic.world.AbstractWorldAdaptor;
import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.SimpleLocation;
import net.momirealms.customcrops.api.mechanic.world.level.*;
@@ -39,6 +39,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -404,7 +405,7 @@ public class WorldManagerImpl implements WorldManager, Listener {
public void removeGlassAt(@NotNull SimpleLocation location) {
CWorld cWorld = loadedWorlds.get(location.getWorldName());
if (cWorld == null) {
LogUtils.warn("Unsupported operation: Removing crop from unloaded world " + location);
LogUtils.warn("Unsupported operation: Removing glass from unloaded world " + location);
return;
}
cWorld.removeGlassAt(location);
@@ -463,20 +464,20 @@ public class WorldManagerImpl implements WorldManager, Listener {
return;
CustomCropsWorld customCropsWorld = optional.get();
ChunkCoordinate chunkCoordinate = ChunkCoordinate.getByBukkitChunk(bukkitChunk);
ChunkPos chunkPos = ChunkPos.getByBukkitChunk(bukkitChunk);
if (customCropsWorld.isChunkLoaded(chunkCoordinate)) {
if (customCropsWorld.isChunkLoaded(chunkPos)) {
return;
}
// load chunks
this.worldAdaptor.loadDynamicData(customCropsWorld, chunkCoordinate);
this.worldAdaptor.loadChunkData(customCropsWorld, chunkPos);
// offline grow part
if (!customCropsWorld.getWorldSetting().isOfflineGrow()) return;
// If chunk data not exists, return
Optional<CustomCropsChunk> optionalChunk = customCropsWorld.getChunkAt(chunkCoordinate);
Optional<CustomCropsChunk> optionalChunk = customCropsWorld.getLoadedChunkAt(chunkPos);
if (optionalChunk.isEmpty()) {
return;
}
@@ -493,9 +494,9 @@ public class WorldManagerImpl implements WorldManager, Listener {
return;
CustomCropsWorld customCropsWorld = optional.get();
ChunkCoordinate chunkCoordinate = ChunkCoordinate.getByBukkitChunk(bukkitChunk);
ChunkPos chunkPos = ChunkPos.getByBukkitChunk(bukkitChunk);
this.worldAdaptor.unloadDynamicData(customCropsWorld, chunkCoordinate);
this.worldAdaptor.unloadChunkData(customCropsWorld, chunkPos);
}
@EventHandler
@@ -509,12 +510,22 @@ public class WorldManagerImpl implements WorldManager, Listener {
}
@Override
public void saveChunkToFile(CustomCropsChunk chunk) {
this.worldAdaptor.saveDynamicData(chunk.getCustomCropsWorld(), chunk);
public void saveChunkToCachedRegion(CustomCropsChunk chunk) {
this.worldAdaptor.saveChunkToCachedRegion(chunk);
}
@Override
public void saveRegionToFile(CustomCropsRegion region) {
this.worldAdaptor.saveRegion(region);
}
@Override
public AbstractWorldAdaptor getWorldAdaptor() {
return worldAdaptor;
}
@Override
public void saveInfoData(CustomCropsWorld customCropsWorld) {
this.worldAdaptor.saveInfoData(customCropsWorld);
}
}

View File

@@ -30,6 +30,7 @@ import net.momirealms.customcrops.api.manager.ConfigManager;
import net.momirealms.customcrops.api.manager.WorldManager;
import net.momirealms.customcrops.api.mechanic.world.*;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsRegion;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import net.momirealms.customcrops.api.mechanic.world.level.WorldInfoData;
import net.momirealms.customcrops.api.mechanic.world.season.Season;
@@ -47,6 +48,7 @@ import org.bukkit.World;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.EventHandler;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
@@ -70,6 +72,15 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
@Override
public void unload(CustomCropsWorld customCropsWorld) {
World world = customCropsWorld.getWorld();
if (world != null) {
new File(world.getWorldFolder(), "customcrops").mkdir();
customCropsWorld.save();
}
}
@Override
public void saveInfoData(CustomCropsWorld customCropsWorld) {
CWorld cWorld = (CWorld) customCropsWorld;
World world = cWorld.getWorld();
if (world == null) {
@@ -77,15 +88,8 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
return;
}
// save world data into psd
world.getPersistentDataContainer().set(key, PersistentDataType.STRING,
gson.toJson(cWorld.getInfoData()));
new File(world.getWorldFolder(), "customcrops").mkdir();
for (CChunk chunk : cWorld.getAllChunksToSave()) {
saveDynamicData(cWorld, chunk);
}
}
@Override
@@ -102,7 +106,7 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
// try converting legacy worlds
if (ConfigManager.convertWorldOnLoad()) {
convertWorld(cWorld, world);
convertWorldFromV33toV34(cWorld, world);
return;
}
@@ -113,7 +117,7 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
}
@Override
public void loadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate) {
public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) {
CWorld cWorld = (CWorld) customCropsWorld;
World world = cWorld.getWorld();
if (world == null) {
@@ -121,35 +125,81 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
return;
}
long time1 = System.currentTimeMillis();
// load lazy chunks firstly
CustomCropsChunk lazyChunk = customCropsWorld.removeLazyChunkAt(chunkCoordinate);
CustomCropsChunk lazyChunk = cWorld.removeLazyChunkAt(chunkPos);
if (lazyChunk != null) {
CChunk cChunk = (CChunk) lazyChunk;
cChunk.setUnloadedSeconds(0);
cWorld.loadChunk(cChunk);
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos + " from lazy chunks");
return;
}
// create or get chunk files
File data = getChunkDataFilePath(world, chunkCoordinate);
if (!data.exists())
// check if region is loaded, load if not loaded
RegionPos regionPos = RegionPos.getByChunkPos(chunkPos);
Optional<CustomCropsRegion> optionalRegion = cWorld.getLoadedRegionAt(regionPos);
if (optionalRegion.isPresent()) {
CustomCropsRegion region = optionalRegion.get();
byte[] bytes = region.getChunkBytes(chunkPos);
if (bytes != null) {
try {
DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(bytes));
CChunk chunk = deserializeChunk(cWorld, dataStream);
dataStream.close();
cWorld.loadChunk(chunk);
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos + " from cached region");
} catch (IOException e) {
LogUtils.severe("Failed to load CustomCrops data at " + chunkPos);
e.printStackTrace();
region.removeChunk(chunkPos);
}
}
return;
// load chunk from local files
long time1 = System.currentTimeMillis();
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(getChunkDataFilePath(world, chunkCoordinate)))) {
}
// if region file not exist, create one
File data = getRegionDataFilePath(world, regionPos);
if (!data.exists()) {
cWorld.loadRegion(new CRegion(cWorld, regionPos));
return;
}
// load region from local files
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(data))) {
DataInputStream dataStream = new DataInputStream(bis);
CChunk chunk = deserialize(cWorld, dataStream);
CRegion region = deserializeRegion(cWorld, dataStream);
dataStream.close();
cWorld.loadChunk(chunk);
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkCoordinate);
cWorld.loadRegion(region);
byte[] bytes = region.getChunkBytes(chunkPos);
if (bytes != null) {
try {
DataInputStream chunkStream = new DataInputStream(new ByteArrayInputStream(bytes));
CChunk chunk = deserializeChunk(cWorld, chunkStream);
chunkStream.close();
cWorld.loadChunk(chunk);
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos);
} catch (IOException e) {
LogUtils.severe("Failed to load CustomCrops data at " + chunkPos + ". Deleting corrupted chunk.");
e.printStackTrace();
region.removeChunk(chunkPos);
}
} else {
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load region " + regionPos);
}
} catch (IOException e) {
LogUtils.severe("Failed to load CustomCrops data at " + chunkCoordinate);
LogUtils.severe("Failed to load CustomCrops region data at " + chunkPos + ". Deleting corrupted region.");
e.printStackTrace();
data.delete();
}
}
@Override
public void unloadDynamicData(CustomCropsWorld ccWorld, ChunkCoordinate chunkCoordinate) {
public void unloadChunkData(CustomCropsWorld ccWorld, ChunkPos chunkPos) {
CWorld cWorld = (CWorld) ccWorld;
World world = cWorld.getWorld();
if (world == null) {
@@ -157,18 +207,35 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
return;
}
cWorld.unloadChunk(chunkCoordinate);
cWorld.unloadChunk(chunkPos);
}
@Override
public void saveDynamicData(CustomCropsWorld ccWorld, CustomCropsChunk chunk) {
public void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk) {
CustomCropsRegion customCropsRegion = customCropsChunk.getCustomCropsRegion();
SerializableChunk serializableChunk = toSerializableChunk((CChunk) customCropsChunk);
if (serializableChunk.canPrune()) {
customCropsRegion.removeChunk(customCropsChunk.getChunkPos());
} else {
customCropsRegion.saveChunk(customCropsChunk.getChunkPos(), serialize(serializableChunk));
}
}
@Override
public void saveRegion(CustomCropsRegion customCropsRegion) {
File file = getRegionDataFilePath(customCropsRegion.getCustomCropsWorld().getWorld(), customCropsRegion.getRegionPos());
if (customCropsRegion.canPrune()) {
file.delete();
return;
}
long time1 = System.currentTimeMillis();
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(getChunkDataFilePath(ccWorld.getWorld(), chunk.getChunkCoordinate())))) {
bos.write(serialize((CChunk) chunk));
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
bos.write(serialize(customCropsRegion));
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to save chunk " + chunk.getChunkCoordinate());
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to save region " + customCropsRegion.getRegionPos());
} catch (IOException e) {
LogUtils.severe("Failed to save CustomCrops data.");
LogUtils.severe("Failed to save CustomCrops region data." + customCropsRegion.getRegionPos());
e.printStackTrace();
}
}
@@ -186,15 +253,35 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
worldManager.unloadWorld(event.getWorld());
}
private String getChunkDataFile(ChunkCoordinate chunkCoordinate) {
return chunkCoordinate.x() + "," + chunkCoordinate.z() + ".ccd";
@EventHandler (ignoreCancelled = true)
public void onWorldSave(WorldSaveEvent event) {
World world = event.getWorld();
worldManager.getCustomCropsWorld(world).ifPresent(CustomCropsWorld::save);
}
private File getChunkDataFilePath(World world, ChunkCoordinate chunkCoordinate) {
@Deprecated
private String getChunkDataFile(ChunkPos chunkPos) {
return chunkPos.x() + "," + chunkPos.z() + ".ccd";
}
private String getRegionDataFile(RegionPos regionPos) {
return "r." + regionPos.x() + "." + regionPos.z() + ".mcc";
}
@Deprecated
private File getChunkDataFilePath(World world, ChunkPos chunkPos) {
if (worldFolder.isEmpty()) {
return new File(world.getWorldFolder(), "customcrops" + File.separator + getChunkDataFile(chunkCoordinate));
return new File(world.getWorldFolder(), "customcrops" + File.separator + getChunkDataFile(chunkPos));
} else {
return new File(worldFolder, world.getName() + File.separator + "customcrops" + File.separator + getChunkDataFile(chunkCoordinate));
return new File(worldFolder, world.getName() + File.separator + "customcrops" + File.separator + getChunkDataFile(chunkPos));
}
}
private File getRegionDataFilePath(World world, RegionPos regionPos) {
if (worldFolder.isEmpty()) {
return new File(world.getWorldFolder(), "customcrops" + File.separator + getRegionDataFile(regionPos));
} else {
return new File(worldFolder, world.getName() + File.separator + "customcrops" + File.separator + getRegionDataFile(regionPos));
}
}
@@ -202,12 +289,51 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
this.worldFolder = folder;
}
public byte[] serialize(CChunk chunk) {
public byte[] serialize(CustomCropsRegion region) {
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
DataOutputStream outStream = new DataOutputStream(outByteStream);
SerializableChunk serializableChunk = toSerializableChunk(chunk);
try {
outStream.writeByte(version);
outStream.writeByte(regionVersion);
outStream.writeInt(region.getRegionPos().x());
outStream.writeInt(region.getRegionPos().z());
Map<ChunkPos, byte[]> map = region.getRegionDataToSave();
outStream.writeInt(map.size());
for (Map.Entry<ChunkPos, byte[]> entry : map.entrySet()) {
outStream.writeInt(entry.getKey().x());
outStream.writeInt(entry.getKey().z());
byte[] dataArray = entry.getValue();
outStream.writeInt(dataArray.length);
outStream.write(dataArray);
}
} catch (IOException e) {
e.printStackTrace();
}
return outByteStream.toByteArray();
}
public CRegion deserializeRegion(CWorld world, DataInputStream dataStream) throws IOException {
int regionVersion = dataStream.readByte();
int regionX = dataStream.readInt();
int regionZ = dataStream.readInt();
RegionPos regionPos = RegionPos.of(regionX, regionZ);
ConcurrentHashMap<ChunkPos, byte[]> map = new ConcurrentHashMap<>();
int chunkAmount = dataStream.readInt();
for (int i = 0; i < chunkAmount; i++) {
int chunkX = dataStream.readInt();
int chunkZ = dataStream.readInt();
ChunkPos chunkPos = ChunkPos.of(chunkX, chunkZ);
byte[] chunkData = new byte[dataStream.readInt()];
dataStream.read(chunkData);
map.put(chunkPos, chunkData);
}
return new CRegion(world, regionPos, map);
}
public byte[] serialize(SerializableChunk serializableChunk) {
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
DataOutputStream outStream = new DataOutputStream(outByteStream);
try {
outStream.writeByte(chunkVersion);
byte[] serializedSections = serializeChunk(serializableChunk);
byte[] compressed = Zstd.compress(serializedSections);
outStream.writeInt(compressed.length);
@@ -219,8 +345,8 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
return outByteStream.toByteArray();
}
public CChunk deserialize(CWorld world, DataInputStream dataStream) throws IOException {
int worldVersion = dataStream.readByte();
public CChunk deserializeChunk(CWorld world, DataInputStream dataStream) throws IOException {
int chunkVersion = dataStream.readByte();
byte[] blockData = readCompressedBytes(dataStream);
return deserializeChunk(world, blockData);
}
@@ -231,7 +357,7 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
// read coordinate
int x = chunkData.readInt();
int z = chunkData.readInt();
ChunkCoordinate coordinate = new ChunkCoordinate(x, z);
ChunkPos coordinate = new ChunkPos(x, z);
// read loading info
int loadedSeconds = chunkData.readInt();
long lastLoadedTime = chunkData.readLong();
@@ -362,10 +488,10 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
}
public SerializableChunk toSerializableChunk(CChunk chunk) {
ChunkCoordinate chunkCoordinate = chunk.getChunkCoordinate();
ChunkPos chunkPos = chunk.getChunkPos();
return new SerializableChunk(
chunkCoordinate.x(),
chunkCoordinate.z(),
chunkPos.x(),
chunkPos.z(),
chunk.getLoadedSeconds(),
chunk.getLastLoadedTime(),
Arrays.stream(chunk.getSectionsForSerialization()).map(this::toSerializableSection).toList(),
@@ -451,7 +577,7 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
return outByteStream.toByteArray();
}
public void convertWorld(@Nullable CWorld cWorld, World world) {
public void convertWorldFromV33toV34(@Nullable CWorld cWorld, World world) {
// handle legacy files
File leagcyFile = new File(world.getWorldFolder(), "customcrops" + File.separator + "data.yml");
if (leagcyFile.exists()) {
@@ -477,11 +603,14 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
LogUtils.warn("Converting chunks for world " + world.getName() + " from 3.3 to 3.4... This might take some time.");
File[] data_files = folder.listFiles();
if (data_files == null) return;
HashMap<RegionPos, CustomCropsRegion> regionHashMap = new HashMap<>();
for (File file : data_files) {
ChunkCoordinate chunkCoordinate = ChunkCoordinate.getByString(file.getName().substring(0, file.getName().length() - 7));
ChunkPos chunkPos = ChunkPos.getByString(file.getName().substring(0, file.getName().length() - 7));
try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) {
CCChunk chunk = (CCChunk) ois.readObject();
CChunk cChunk = new CChunk(cWorld, chunkCoordinate);
CChunk cChunk = new CChunk(cWorld, chunkPos);
for (net.momirealms.customcrops.api.object.world.SimpleLocation legacyLocation : chunk.getGreenhouseSet()) {
SimpleLocation simpleLocation = new SimpleLocation(legacyLocation.getWorldName(), legacyLocation.getX(), legacyLocation.getY(), legacyLocation.getZ());
cChunk.addGlassAt(new MemoryGlass(simpleLocation), simpleLocation);
@@ -506,19 +635,56 @@ public class BukkitWorldAdaptor extends AbstractWorldAdaptor {
Fertilizer fertilizer = entry.getValue().getFertilizer();
cChunk.addPotAt(new MemoryPot(simpleLocation, entry.getValue().getKey(), entry.getValue().getWater(), fertilizer == null ? "" : fertilizer.getKey(), fertilizer == null ? 0 : fertilizer.getTimes()), simpleLocation);
}
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(getChunkDataFilePath(world, cChunk.getChunkCoordinate())))) {
bos.write(serialize(cChunk));
} catch (IOException e) {
LogUtils.severe("Failed to save CustomCrops data.");
e.printStackTrace();
CustomCropsRegion region = regionHashMap.get(chunkPos.getRegionPos());
if (region == null) {
region = new CRegion(cWorld, chunkPos.getRegionPos());
regionHashMap.put(chunkPos.getRegionPos(), region);
}
region.saveChunk(chunkPos, serialize(toSerializableChunk(cChunk)));
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
LogUtils.info("Error at " + file.getAbsolutePath());
}
}
LogUtils.info("Successfully converted chunks for world: " + world);
for (CustomCropsRegion region : regionHashMap.values()) {
saveRegion(region);
}
LogUtils.info("Successfully converted chunks for world: " + world.getName());
}
}
public void convertWorldFromV342toV343(@Nullable CWorld cWorld, World world) {
File folder = new File(world.getWorldFolder(), "customcrops");
if (!folder.exists()) return;
LogUtils.warn("Converting chunks for world " + world.getName() + " from 3.4.2 to 3.4.3... This might take some time.");
File[] data_files = folder.listFiles();
if (data_files == null) return;
HashMap<RegionPos, CustomCropsRegion> regionHashMap = new HashMap<>();
for (File file : data_files) {
String fileName = file.getName();
if (fileName.endsWith(".ccd")) {
String chunkStr = file.getName().substring(0, fileName.length()-4);
ChunkPos chunkPos = ChunkPos.getByString(chunkStr);
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
DataInputStream dataStream = new DataInputStream(bis);
byte[] chunkData = dataStream.readAllBytes();
dataStream.close();
CustomCropsRegion region = regionHashMap.get(chunkPos.getRegionPos());
if (region == null) {
region = new CRegion(cWorld, chunkPos.getRegionPos());
regionHashMap.put(chunkPos.getRegionPos(), region);
}
region.saveChunk(chunkPos, chunkData);
} catch (IOException e) {
e.printStackTrace();
}
file.delete();
}
}
for (CustomCropsRegion region : regionHashMap.values()) {
saveRegion(region);
}
LogUtils.info("Successfully converted chunks for world: " + world.getName());
}
}

View File

@@ -23,10 +23,11 @@ import com.infernalsuite.aswm.api.events.LoadSlimeWorldEvent;
import com.infernalsuite.aswm.api.world.SlimeWorld;
import net.momirealms.customcrops.api.CustomCropsPlugin;
import net.momirealms.customcrops.api.manager.WorldManager;
import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate;
import net.momirealms.customcrops.api.mechanic.world.BlockPos;
import net.momirealms.customcrops.api.mechanic.world.ChunkPos;
import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsRegion;
import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld;
import net.momirealms.customcrops.api.mechanic.world.level.WorldInfoData;
import net.momirealms.customcrops.api.util.LogUtils;
@@ -65,13 +66,13 @@ public class SlimeWorldAdaptor extends BukkitWorldAdaptor {
}
@Override
public void unload(CustomCropsWorld customCropsWorld) {
public void saveInfoData(CustomCropsWorld customCropsWorld) {
SlimeWorld slimeWorld = slimePlugin.getWorld(customCropsWorld.getWorldName());
if (slimeWorld == null) {
super.unload(customCropsWorld);
super.saveInfoData(customCropsWorld);
return;
}
CWorld cWorld = (CWorld) customCropsWorld;
Optional<CompoundTag> optionalCompoundTag = slimeWorld.getExtraData().getAsCompoundTag("customcrops");
if (optionalCompoundTag.isEmpty()) {
LogUtils.warn("Failed to unload data for world " + customCropsWorld.getWorldName() + " because slime world format is incorrect.");
@@ -80,9 +81,16 @@ public class SlimeWorldAdaptor extends BukkitWorldAdaptor {
CompoundMap ccDataMap = optionalCompoundTag.get().getValue();
ccDataMap.put(new StringTag("world-info", gson.toJson(customCropsWorld.getInfoData())));
for (CChunk chunk : cWorld.getAllChunksToSave()) {
ccDataMap.put(chunkToTag(chunk));
}
@Override
public void unload(CustomCropsWorld customCropsWorld) {
SlimeWorld slimeWorld = slimePlugin.getWorld(customCropsWorld.getWorldName());
if (slimeWorld == null) {
super.unload(customCropsWorld);
return;
}
customCropsWorld.save();
}
@Override
@@ -108,64 +116,97 @@ public class SlimeWorldAdaptor extends BukkitWorldAdaptor {
}
@Override
public void loadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate) {
public void loadChunkData(CustomCropsWorld customCropsWorld, ChunkPos chunkPos) {
SlimeWorld slimeWorld = slimePlugin.getWorld(customCropsWorld.getWorldName());
if (slimeWorld == null) {
super.loadDynamicData(customCropsWorld, chunkCoordinate);
super.loadChunkData(customCropsWorld, chunkPos);
return;
}
long time1 = System.currentTimeMillis();
CWorld cWorld = (CWorld) customCropsWorld;
// load lazy chunks firstly
CustomCropsChunk lazyChunk = customCropsWorld.removeLazyChunkAt(chunkCoordinate);
CustomCropsChunk lazyChunk = customCropsWorld.removeLazyChunkAt(chunkPos);
if (lazyChunk != null) {
CChunk cChunk = (CChunk) lazyChunk;
cChunk.setUnloadedSeconds(0);
cWorld.loadChunk(cChunk);
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos + " from lazy chunks");
return;
}
Optional<CompoundTag> optionalCompoundTag = slimeWorld.getExtraData().getAsCompoundTag("customcrops");
if (optionalCompoundTag.isEmpty()) {
LogUtils.warn("Failed to load data for " + chunkCoordinate + " in world " + customCropsWorld.getWorldName() + " because slime world format is incorrect.");
LogUtils.warn("Failed to load data for " + chunkPos + " in world " + customCropsWorld.getWorldName() + " because slime world format is incorrect.");
return;
}
Tag<?> chunkTag = optionalCompoundTag.get().getValue().get(chunkCoordinate.toString());
if (chunkTag == null) return;
Tag<?> chunkTag = optionalCompoundTag.get().getValue().get(chunkPos.getAsString());
if (chunkTag == null) {
return;
}
Optional<CompoundTag> chunkCompoundTag = chunkTag.getAsCompoundTag();
if (chunkCompoundTag.isEmpty()) return;
if (chunkCompoundTag.isEmpty()) {
return;
}
// load chunk from local files
long time1 = System.currentTimeMillis();
// load chunk from slime world
cWorld.loadChunk(tagToChunk(cWorld, chunkCompoundTag.get()));
long time2 = System.currentTimeMillis();
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkCoordinate);
CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkPos);
}
@Override
public void saveDynamicData(CustomCropsWorld customCropsWorld, CustomCropsChunk chunk) {
SlimeWorld slimeWorld = getSlimeWorld(customCropsWorld.getWorldName());
public void saveChunkToCachedRegion(CustomCropsChunk customCropsChunk) {
CustomCropsWorld customCropsWorld = customCropsChunk.getCustomCropsWorld();
SlimeWorld slimeWorld = getSlimeWorld(customCropsChunk.getCustomCropsWorld().getWorldName());
if (slimeWorld == null) {
super.saveDynamicData(customCropsWorld, chunk);
super.saveChunkToCachedRegion(customCropsChunk);
return;
}
Optional<CompoundTag> optionalCompoundTag = slimeWorld.getExtraData().getAsCompoundTag("customcrops");
if (optionalCompoundTag.isEmpty()) {
LogUtils.warn("Failed to save data for " + chunk + " in world " + customCropsWorld.getWorldName() + " because slime world format is incorrect.");
LogUtils.warn("Failed to save data for " + customCropsChunk.getChunkPos() + " in world " + customCropsWorld.getWorldName() + " because slime world format is incorrect.");
return;
}
CustomCropsPlugin.get().getScheduler().runTaskSync(() -> optionalCompoundTag.get().getValue().put(chunkToTag((CChunk) chunk)), null);
SerializableChunk serializableChunk = toSerializableChunk((CChunk) customCropsChunk);
if (Bukkit.isPrimaryThread()) {
if (serializableChunk.canPrune()) {
optionalCompoundTag.get().getValue().remove(customCropsChunk.getChunkPos().getAsString());
} else {
optionalCompoundTag.get().getValue().put(chunkToTag(serializableChunk));
}
} else {
CustomCropsPlugin.get().getScheduler().runTaskSync(() -> {
if (serializableChunk.canPrune()) {
optionalCompoundTag.get().getValue().remove(customCropsChunk.getChunkPos().getAsString());
} else {
optionalCompoundTag.get().getValue().put(chunkToTag(serializableChunk));
}
}, null);
}
}
@Override
public void saveRegion(CustomCropsRegion customCropsRegion) {
CustomCropsWorld customCropsWorld = customCropsRegion.getCustomCropsWorld();
SlimeWorld slimeWorld = getSlimeWorld(customCropsWorld.getWorldName());
if (slimeWorld == null) {
super.saveRegion(customCropsRegion);
return;
}
// don't need to save region to slime world
}
private SlimeWorld getSlimeWorld(String name) {
return slimePlugin.getWorld(name);
}
private CompoundTag chunkToTag(CChunk chunk) {
SerializableChunk serializableChunk = toSerializableChunk(chunk);
private CompoundTag chunkToTag(SerializableChunk serializableChunk) {
CompoundMap map = new CompoundMap();
map.put(new IntTag("x", serializableChunk.getX()));
map.put(new IntTag("z", serializableChunk.getZ()));
@@ -178,7 +219,7 @@ public class SlimeWorldAdaptor extends BukkitWorldAdaptor {
sectionMap.put(new ListTag<>(String.valueOf(section.getSectionID()), TagType.TAG_COMPOUND, section.getBlocks()));
}
map.put(new CompoundTag("sections", sectionMap));
return new CompoundTag(chunk.getChunkCoordinate().toString(), map);
return new CompoundTag(serializableChunk.getX() + "," + serializableChunk.getZ(), map);
}
private CChunk tagToChunk(CWorld cWorld, CompoundTag tag) {
@@ -186,7 +227,7 @@ public class SlimeWorldAdaptor extends BukkitWorldAdaptor {
CompoundMap map = tag.getValue();
int x = map.get("x").getAsIntTag().get().getValue();
int z = map.get("z").getAsIntTag().get().getValue();
ChunkCoordinate coordinate = new ChunkCoordinate(x, z);
ChunkPos coordinate = new ChunkPos(x, z);
int loadedSeconds = map.get("loadedSeconds").getAsIntTag().get().getValue();
long lastLoadedTime = map.get("lastLoadedTime").getAsLongTag().get().getValue();
int[] queued = map.get("queued").getAsIntArrayTag().get().getValue();

View File

@@ -1,5 +1,5 @@
# Don't change
config-version: '35'
config-version: '36'
# Debug
debug: false