Javadocs, cleanup, extension's client rules, built-in BundledModules generators, etc (#544)

* Add javadocs to many methods

* Added built-in ways to generate BundledModules and documented them

* Add Eclipse launch files to .gitignore and fix 11 warnings

* Remove many unused imports

* Remove some dead and unused code around app loading

* Allow extensions to have client-side rules in their SettingsManager

Also change tabs to spaces when those changed

* Remove unnecessary classloader param in BundledModule

* Change max tickwarp duration to Integer.MAX_VALUE

* Move RulePrinter into the source so it can be managed by the IDE

* Properly reset hopperCounters when changing worlds

Also change tabs to spaces again and minor tweaks to javadocs

* Run mergedocs even if Full.md was modified

* Update src/main/java/carpet/commands/TickCommand.java

Co-authored-by: gnembon <41132274+gnembon@users.noreply.github.com>
This commit is contained in:
altrisiandgnembon authored and GitHub committed 2020-11-16 09:30:59 -04:00
1 parent f444fab183
commit ba1071a8d1
35 files changed
+315 -91

No files matched your search

-1
View File
@@ -5,7 +5,6 @@ on:
- master
paths:
- "docs/**"
- "!docs/scarpet/Full.md"
jobs:
Scarpet:
runs-on: ubuntu-latest
-19
View File
@@ -21,25 +21,6 @@ jobs:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: ${{ runner.os }}-gradle
- name: Generate RulePrinter class
run: |
cd src/main/java/carpet/utils
echo "
package carpet.utils;
import carpet.CarpetServer;
import carpet.settings.SettingsManager;
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
import java.lang.System;
public class RulePrinter implements PreLaunchEntrypoint {
@Override
public void onPreLaunch() {
CarpetServer.onGameStarted();
CarpetServer.settingsManager.printAllRulesToLog(null);
System.exit(0);
}
}
" > RulePrinter.java
cd ../../../../../
- name: Replace fabric.mod.json
run: |
cd src/main/resources
+3
View File
@@ -13,6 +13,9 @@ publish/
*.ipr
*.iws
# eclipse
*.launch
# vscode
.settings/
+57 -3
View File
@@ -20,50 +20,104 @@ public interface CarpetExtension
* Runs once per loaded world once the server / gamerules etc are fully loaded
* but before worlds are loaded
* Can be loaded multiple times in SinglePlayer
*
* @param server The {@link MinecraftServer} instance that loaded
*/
default void onServerLoaded(MinecraftServer server) {}
/**
* Runs once per loaded world once the World files are fully loaded
* Can be loaded multiple times in SinglePlayer
*
* @param server The current {@link MinecraftServer} instance
*
*/
default void onServerLoadedWorlds(MinecraftServer server) {}
/**
* Runs once per game tick, as a first thing in the tick
*
* @param server The current {@link MinecraftServer} instance
*
*/
default void onTick(MinecraftServer server) {}
/**
* Register your own commands right after vanilla commands are added
* If that matters for you
*
* @param dispatcher The current {@link CommandSource<ServerCommandSource>} dispatcher
* where you should register your commands
*
*/
default void registerCommands(CommandDispatcher<ServerCommandSource> dispatcher) {}
/**
* Provide your own custom settings manager managed in the same way as base /carpet
* command, but separated to its own command as defined in SettingsManager.
*
* @return Your custom {@link SettingsManager} instance to be managed by Carpet
*
*/
default SettingsManager customSettingsManager() {return null;}
/**
* todiddalidoo
* Event that gets called when a player logs in
*
* @param player The {@link ServerPlayerEntity} that logged in
*
*/
default void onPlayerLoggedIn(ServerPlayerEntity player) {}
/**
* todiddalidoo
* Event that gets called when a player logs out
*
* @param player The {@link ServerPlayerEntity} that logged out
*
*/
default void onPlayerLoggedOut(ServerPlayerEntity player) {}
/**
* Event that gets called when the server closes.
* Can be called multiple times in singleplayer
*
* @param server The {@link MinecraftServer} that is closing
*
*/
default void onServerClosed(MinecraftServer server) {}
/**
* Event that gets called when the server reloads (usually
* when running the /reload command)
*
* @param server The {@link MinecraftServer} that is reloading
*
*/
default void onReload(MinecraftServer server) {}
/**
* Event that gets called when a player logs in
*
* @return A {@link String} usually being the extension's id
*
*/
default String version() {return null;}
/**
* Called when registering custom loggers
*
*/
default void registerLoggers() {}
/**
* Method for Carpet to get the translations for extension's
* rules.
*
* @param lang A {@link String} being the language id selected by the user
* @return A {@link Map<String, String>} containing the string key with it's
* respective translation {@link String} or {@link null} if not available
*
*/
default Map<String, String> canHasTranslations(String lang) { return null;}
/**
@@ -79,7 +133,7 @@ public interface CarpetExtension
*
* EntityEventsGroup.Event class: to handle `entity_event('foo', ...)` type of events
*
* @param expression: Passed CarpetExpression to add built-in functions to
* @param expression Passed {@link CarpetExpression} to add built-in functions to
*/
default void scarpetApi(CarpetExpression expression) {}
+8
View File
@@ -7,6 +7,7 @@ import java.util.Random;
import carpet.commands.*;
import carpet.network.ServerNetworkHandler;
import carpet.helpers.HopperCounter;
import carpet.helpers.TickSpeed;
import carpet.logging.LoggerRegistry;
import carpet.script.CarpetScriptServer;
@@ -44,6 +45,12 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit
}
// Separate from onServerLoaded, because a server can be loaded multiple times in singleplayer
/**
* Registers a {@link CarpetExtension} to be managed by Carpet.<br>
* Should be called before Carpet's startup, like in Fabric Loader's
* {@link net.fabricmc.api.ModInitializer} entrypoint
* @param extension The instance of a {@link CarpetExtension} to be registered
*/
public static void manageExtension(CarpetExtension extension)
{
extensions.add(extension);
@@ -85,6 +92,7 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit
{
extensions.forEach(e -> e.onServerLoadedWorlds(minecraftServer));
scriptServer.initializeForWorld();
HopperCounter.resetAll(minecraftServer);
}
public static void tick(MinecraftServer server)
@@ -35,7 +35,7 @@ public class TickCommand
executes((c) -> setTps(c.getSource(), getFloat(c, "rate"))))).
then(literal("warp").
executes( (c)-> setWarp(c.getSource(), 0, null)).
then(argument("ticks", integer(0,4000000)).
then(argument("ticks", integer(0)).
suggests( (c, b) -> suggestMatching(new String[]{"3600","72000"},b)).
executes((c) -> setWarp(c.getSource(), getInteger(c,"ticks"), null)).
then(argument("tail command", greedyString()).
@@ -143,4 +143,3 @@ public class TickCommand
}
}
@@ -1,7 +1,6 @@
package carpet.mixins;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.script.api.Auxiliary;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
@@ -2,6 +2,7 @@ package carpet.mixins;
import carpet.CarpetServer;
import carpet.network.CarpetClient;
import carpet.settings.SettingsManager;
import net.minecraft.client.network.ClientPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -23,6 +24,10 @@ public class ClientPlayerEntity_clientCommandMixin
{
ClientPlayerEntity playerSource = (ClientPlayerEntity)(Object) this;
CarpetServer.settingsManager.inspectClientsideCommand(playerSource.getCommandSource(), string);
CarpetServer.extensions.forEach(e -> {
SettingsManager sm = e.customSettingsManager();
if (sm != null) sm.inspectClientsideCommand(playerSource.getCommandSource(), string);
});
}
}
}
@@ -2,13 +2,11 @@ package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.InfestedBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -19,7 +19,6 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Mixin(PistonBlock.class)
public abstract class PistonBlock_movableTEMixin extends FacingBlock
@@ -3,14 +3,9 @@ package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MovementType;
import net.minecraft.entity.player.PlayerAbilities;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
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.Redirect;
@@ -2,7 +2,6 @@ package carpet.mixins;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.world.dimension.DimensionType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -8,7 +8,6 @@ import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -2,7 +2,6 @@ package carpet.mixins;
import carpet.utils.RandomTools;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@@ -2,19 +2,13 @@ package carpet.mixins;
import carpet.fakes.ServerChunkManagerInterface;
import carpet.utils.SpawnReporter;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.server.world.ChunkTicketManager;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.SpawnHelper;
import net.minecraft.world.World;
import net.minecraft.world.WorldProperties;
import net.minecraft.world.chunk.WorldChunk;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.level.LevelProperties;
import org.apache.commons.lang3.tuple.Pair;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@@ -23,7 +17,6 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.HashMap;
import java.util.HashSet;
@@ -17,12 +17,9 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import com.google.common.collect.Lists;
@@ -1,11 +1,8 @@
package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.network.ClientConnection;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -1,11 +1,8 @@
package carpet.mixins;
import carpet.CarpetServer;
import net.minecraft.network.ClientConnection;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -6,7 +6,6 @@ import com.mojang.authlib.GameProfile;
import net.minecraft.entity.Entity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.stat.Stat;
@@ -4,7 +4,6 @@ import carpet.fakes.ServerPlayerInteractionManagerInterface;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.network.ServerPlayerInteractionManager;
@@ -7,7 +7,6 @@ import net.minecraft.util.profiler.Profiler;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.village.raid.RaidManager;
import net.minecraft.world.MutableWorldProperties;
import net.minecraft.world.WanderingTraderManager;
import net.minecraft.world.World;
import net.minecraft.world.border.WorldBorder;
import net.minecraft.world.dimension.DimensionType;
@@ -18,7 +17,6 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.BiFunction;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
@@ -2,7 +2,6 @@ package carpet.mixins;
import carpet.fakes.SpawnHelperInnerInterface;
import carpet.utils.SpawnReporter;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.util.math.GravityField;
@@ -5,22 +5,14 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LightningEntity;
import net.minecraft.entity.mob.SkeletonHorseEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.text.TranslatableText;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.command.SummonCommand;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules;
import net.minecraft.world.LocalDifficulty;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(SummonCommand.class)
public class SummonCommandMixin
@@ -10,7 +10,6 @@ import net.minecraft.server.world.ChunkHolder;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.world.BlockView;
import net.minecraft.world.MutableWorldProperties;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.World;
import net.minecraft.world.chunk.EmptyChunk;
@@ -50,6 +50,13 @@ public class CarpetScriptServer
private static final List<Module> bundledModuleData = new ArrayList<>();
private static final List<Module> ruleModuleData = new ArrayList<>();
/**
* Registers a Scarpet App to be always available to be loaded
* in the /script load list.
* @see BundledModule#fromPath(ClassLoader, String, String, boolean)
*
* @param app The {@link BundledModule} of the app
*/
public static void registerBuiltInScript(BundledModule app)
{
bundledModuleData.add(app);
@@ -57,10 +64,11 @@ public class CarpetScriptServer
/**
* Registers a Scarpet App to be used as a Rule App
* (to be controlled with the value of a Carpet rule)
* (to be controlled with the value of a Carpet rule).
* Libraries should be registered with {@link #registerBuiltInScript(BundledModule)} instead
* @see BundledModule#fromPath(ClassLoader, String, String, boolean)
*
* @param app is the BundledModule of an app. Libraries
* should be registered as builtInScripts instead
* @param app The {@link BundledModule} of the app.
*/
public static void registerSettingsApp(BundledModule app) {
ruleModuleData.add(app);
@@ -214,13 +222,6 @@ public class CarpetScriptServer
// Messenger.m(source, "r Failed to add "+name+" app: invalid app config (via '__config()' function)");
// return false;
//}
if (module == null)
{
Messenger.m(source, "r Unable to locate the app, but created empty "+name+" app instead");
modules.put(name, newHost);
return true;
}
String code = module.getCode();
if (module.getCode() == null)
{
Messenger.m(source, "r Unable to load "+name+" app - not found");
-1
View File
@@ -1,7 +1,6 @@
package carpet.script;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.Value;
import java.util.ArrayList;
@@ -17,14 +17,46 @@ public class BundledModule extends Module
this.name = name;
this.code = code;
}
/**
* Creates a new {@link BundledModule} with an app located in Carpet's script storage.
* @param scriptName A {@link String} being the name of the script. The extension will be autocompleted
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
* @return The created {@link BundledModule}
*/
public static BundledModule carpetNative(String scriptName, boolean isLibrary)
{
BundledModule module = new BundledModule(null, null, isLibrary);
try
{
module.name = scriptName.toLowerCase(Locale.ROOT);
return fromPath("assets/carpet/scripts/", scriptName, isLibrary);
}
/**
* Creates a new {@link BundledModule} with an app located at a specified place.
* @see #fromPathWithCustomName(ClassLoader, String, String, boolean)
*
* @param path A {@link String} being the path to the directory where the app is located.
* @param scriptName A {@link String} being the name of the script. The extension will be autocompleted
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
* @return The created {@link BundledModule}
*/
public static BundledModule fromPath(String path, String scriptName, boolean isLibrary) {
return fromPathWithCustomName(path+scriptName+(isLibrary?".scl":".sc"), scriptName, isLibrary);
}
/**
* Creates a new {@link BundledModule} with an app located at the specified fullPath with a custom name.
* @see #fromPath(ClassLoader, String, String, boolean)
*
* @param fullPath A {@link String} being the full path to the app's code, including file and extension.
* @param scriptName A {@link String} being the custom name for the script.
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
* @return The created {@link BundledModule}
*/
public static BundledModule fromPathWithCustomName(String fullPath, String customName, boolean isLibrary) {
BundledModule module = new BundledModule(null, null, isLibrary);
try
{
module.name = customName.toLowerCase(Locale.ROOT);
module.code = IOUtils.toString(
BundledModule.class.getClassLoader().getResourceAsStream("assets/carpet/scripts/"+scriptName+(isLibrary?".scl":".sc")),
BundledModule.class.getClassLoader().getResourceAsStream(fullPath),
StandardCharsets.UTF_8
);
}
@@ -1,7 +1,6 @@
package carpet.script.value;
import carpet.script.CarpetContext;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
@@ -13,7 +13,6 @@ import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.LongTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.registry.Registry;
import java.util.ArrayList;
import java.util.Arrays;
@@ -25,7 +24,6 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static carpet.script.value.NBTSerializableValue.nameFromRegistryId;
import static java.lang.Math.abs;
public class ListValue extends AbstractListValue implements ContainerValueInterface
@@ -2,7 +2,6 @@ package carpet.script.value;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.Tokenizer;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.ExpressionException;
@@ -16,6 +16,14 @@ import java.util.Set;
import static carpet.utils.Translations.tr;
/**
* A parsed Carpet rule, with its field, name, value, and other useful stuff.
*
* It is generated from the fields with the {@link Rule} annotation
* when being parsed by {@link SettingsManager#parseSettingsClass(Class)}.
*
* @param <T> The field's type
*/
public final class ParsedRule<T> implements Comparable<ParsedRule> {
public final Field field;
public final String name;
@@ -177,6 +185,9 @@ public final class ParsedRule<T> implements Comparable<ParsedRule> {
return this;
}
/**
* @return The value of this {@link ParsedRule}, in its type
*/
public T get()
{
try
@@ -189,11 +200,19 @@ public final class ParsedRule<T> implements Comparable<ParsedRule> {
}
}
/**
* @return The value of this {@link ParsedRule}, as a {@link String}
*/
public String getAsString()
{
return convertToString(get());
}
/**
* @return The value of this {@link ParsedRule}, converted to a {@link boolean}.
* It will only return {@link true} if it's a true {@link boolean} or
* a number greater than zero.
*/
public boolean getBoolValue()
{
if (type == boolean.class) return (Boolean) get();
@@ -201,11 +220,17 @@ public final class ParsedRule<T> implements Comparable<ParsedRule> {
return false;
}
/**
* @return Wether or not this {@link ParsedRule} is in its default value
*/
public boolean isDefault()
{
return defaultValue.equals(get());
}
/**
* Resets this rule to its default value
*/
public void resetToDefault(ServerCommandSource source)
{
set(source, defaultValue, defaultAsString);
@@ -247,16 +272,28 @@ public final class ParsedRule<T> implements Comparable<ParsedRule> {
return String.format("rule.%s.name", name);
}
/**
* @return A {@link String} being the translated {@link ParsedRule#name} of this rule,
* in Carpet's configured language.
*/
public String translatedName(){
String key = translationKey();
return Translations.hasTranslation(key) ? tr(key) + String.format(" (%s)", name): name;
}
/**
* @return A {@link String} being the translated {@link ParsedRule#description} of this rule,
* in Carpet's configured language.
*/
public String translatedDescription()
{
return tr(String.format("rule.%s.desc", (name)), description);
}
/**
* @return A {@link String} being the translated {@link ParsedRule#extraInfo} of this
* {@link ParsedRule}, in Carpet's configured language.
*/
public List<String> translatedExtras()
{
if (!Translations.hasTranslations()) return extraInfo;
@@ -12,6 +12,11 @@ public class RuleCategory
public static final String TNT = "tnt";
public static final String DISPENSER = "dispenser";
public static final String SCARPET = "scarpet";
/**
* Rules with this {@link RuleCategory} will have a client-side
* counterpart, so they can be set independently without the server
* being Carpet's
*/
public static final String CLIENT = "client";
}
@@ -48,9 +48,19 @@ import static net.minecraft.command.CommandSource.suggestMatching;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
/**
* Manages and parses Carpet rules with their own command.
* @see #SettingsManager(String, String, String)
*
*/
public class SettingsManager
{
private Map<String, ParsedRule<?>> rules = new HashMap<>();
/**
* Whether or not this {@link SettingsManager} is locked. That is specified
* by writing "locked" at the beginning of the settings file. May not
* correctly react to changes.
*/
public boolean locked;
private final String version;
private final String identifier;
@@ -59,7 +69,13 @@ public class SettingsManager
private List<TriConsumer<ServerCommandSource, ParsedRule<?>, String>> observers = new ArrayList<>();
private static List<TriConsumer<ServerCommandSource, ParsedRule<?>, String>> staticObservers = new ArrayList<>();
/**
* Creates a new {@link SettingsManager} without a fancy name.
* @see #SettingsManager(String, String, String)
*
* @param version A {@link String} with the mod's version
* @param identifier A {@link String} with the mod's id, will be the command name
*/
public SettingsManager(String version, String identifier)
{
this.version = version;
@@ -67,6 +83,14 @@ public class SettingsManager
this.fancyName = identifier;
}
/**
* Creates a new {@link SettingsManager} with a fancy name
* @see #SettingsManager(String, String)
*
* @param version A {@link String} with the mod's version
* @param identifier A {@link String} with the mod's id, will be the command name
* @param fancyName A {@link String} being the mod's fancy name.
*/
public SettingsManager(String version, String identifier, String fancyName)
{
this.version = version;
@@ -74,22 +98,41 @@ public class SettingsManager
this.fancyName = fancyName;
}
/**
* @return A {@link String} being this {@link SettingsManager}'s
* identifier, which is also the command name
*/
public String getIdentifier() {
return identifier;
}
/**
* Attaches a {@link MinecraftServer} to this {@link SettingsManager}.<br>
* This is handled automatically by Carpet
* @param server The {@link MinecraftServer} instance to be attached
*/
public void attachServer(MinecraftServer server)
{
this.server = server;
loadConfigurationFromConf();
}
/**
* Detaches the {@link MinecraftServer} of this {@link SettingsManager} and
* resets its {@link ParsedRule}s to their default values.<br>
* This is handled automatically by Carpet
*/
public void detachServer()
{
for (ParsedRule<?> rule : rules.values()) rule.resetToDefault(null);
server = null;
}
/**
* Adds all annotated fields with the {@link Rule} annotation
* to this {@link SettingsManager} in order to handle them.
* @param settingsClass The class that will be analyzed
*/
public void parseSettingsClass(Class settingsClass)
{
for (Field f : settingsClass.getDeclaredFields())
@@ -101,10 +144,31 @@ public class SettingsManager
}
}
/**
* Adds a custom rule observer to changes in rules from
* <b>this specific</b> {@link SettingsManager} instance.
* @see SettingsManager#addGlobalRuleObserver(TriConsumer)
*
* @param observer A {@link TriConsumer} that will be called with
* the used {@link ServerCommandSource}, the changed
* {@link ParsedRule} and a {@link String} being the
* value that the user typed.
*/
public void addRuleObserver(TriConsumer<ServerCommandSource, ParsedRule<?>, String> observer)
{
observers.add(observer);
}
/**
* Adds a custom rule observer to changes in rules from
* <b>any</b> registered {@link SettingsManger} instance.
* @see SettingsManager#addRuleObserver(TriConsumer)
*
* @param observer A {@link TriConsumer} that will be called with
* the used {@link ServerCommandSource}, the changed
* {@link ParsedRule} and a {@link String} being the
* value that the user typed.
*/
public static void addGlobalRuleObserver(TriConsumer<ServerCommandSource, ParsedRule<?>, String> observer)
{
staticObservers.add(observer);
@@ -133,6 +197,10 @@ public class SettingsManager
}
}
/**
* Initializes Scarpet rules in this {@link SettingsManager}, if any.<br>
* This is handled automatically by Carpet.
*/
public void initializeScarpetRules() {
for (ParsedRule<?> rule : rules.values())
{
@@ -142,6 +210,10 @@ public class SettingsManager
}
}
/**
* @return An {@link Iterable} list of all categories
* from rules in this {@link SettingsManager}
*/
public Iterable<String> getCategories()
{
Set<String> categories = new HashSet<>();
@@ -150,16 +222,29 @@ public class SettingsManager
}
/**
* Gets a registered rule in this {@link SettingsManager} by its name.
* @param name The rule name
* @return The {@link ParsedRule} with that name
*/
public ParsedRule<?> getRule(String name)
{
return rules.get(name);
}
/**
* @return A {@link Collection} of the registered rules in this
* {@link SettingsManager}.
*/
public Collection<ParsedRule<?>> getRules()
{
return rules.values().stream().sorted().collect(Collectors.toList());
}
/**
* @return A {@link Collection} of {@link ParsedRule}s that have defaults
* for this world different than the rule's default value.
*/
public Collection<ParsedRule<?>> findStartupOverrides()
{
Set<String> defaults = readSettingsFromConf().getLeft().keySet();
@@ -167,7 +252,10 @@ public class SettingsManager
sorted().collect(Collectors.toList());
}
/**
* @return A collection of {@link ParsedRule} that are not in
* their default value
*/
public Collection<ParsedRule<?>> getNonDefault()
{
return rules.values().stream().filter(r -> !r.isDefault()).sorted().collect(Collectors.toList());
@@ -178,6 +266,10 @@ public class SettingsManager
return server.getSavePath(WorldSavePath.ROOT).resolve(identifier+".conf").toFile();
}
/**
* Disables all {@link ParsedRule} with the {@link RuleCategory#COMMAND} category,
* called when the {@link SettingsManager} is {@link #locked}.
*/
public void disableBooleanCommands()
{
for (ParsedRule<?> rule : rules.values())
@@ -213,6 +305,9 @@ public class SettingsManager
///todo is it really needed? resendCommandTree();
}
/**
* Notifies all players that the commands changed by resending the command tree.
*/
public void notifyPlayersCommandsChanged()
{
if (server == null || server.getPlayerManager() == null)
@@ -228,6 +323,15 @@ public class SettingsManager
}));
}
/**
* Returns whether the the {@link ServerCommandSource} can execute
* a command given the required permission level, according to
* Carpet's standard for permissions.
* @param source The origin {@link ServerCommandSource}
* @param commandLevel A {@link String} being the permission level (either 0-4, a
* {@link boolean} value or "ops".
* @return Whether or not the {@link ServerCommandSource} meets the required level
*/
public static boolean canUseCommand(ServerCommandSource source, String commandLevel)
{
switch (commandLevel)
@@ -245,6 +349,12 @@ public class SettingsManager
return false;
}
/**
* @param commandLevel A {@link String} being a permission level according to
* Carpet's standard for permissions (either 0-4, a {@link boolean},
* or "ops".
* @return An {@link int} with the translated Vanilla permission level
*/
public static int getCommandLevel(String commandLevel)
{
switch (commandLevel)
@@ -347,6 +457,15 @@ public class SettingsManager
}).sorted().collect(ImmutableList.toImmutableList());
}
/**
* A method to pretty print in markdown (useful for Github wiki/readme) the current
* registered rules for a category to the log. Contains the name, description,
* categories, type, defaults, wether or not they are strict, their suggested
* values, and the descriptions of their validators.
* @param category A {@link String} being the category to print, {@link null} to print
* all registered rules.
* @return {@link int} 1 if things didn't fail, and all the info to System.out
*/
public int printAllRulesToLog(String category)
{
PrintStream ps = System.out;
@@ -393,6 +512,11 @@ public class SettingsManager
return rule;
}
/**
* Registers the the settings command for this {@link SettingsManager}.<br>
* It is handled automatically by Carpet.
* @param dispatcher The current {@link CommandDispatcher}
*/
public void registerCommand(CommandDispatcher<ServerCommandSource> dispatcher)
{
if (dispatcher.getRoot().getChildren().stream().anyMatch(node -> node.getName().equalsIgnoreCase(identifier)))
@@ -584,7 +708,7 @@ public class SettingsManager
public void inspectClientsideCommand(ServerCommandSource source, String string)
{
if (string.startsWith("/carpet "))
if (string.startsWith("/"+identifier+" "))
{
String[] res = string.split("\\s+", 3);
if (res.length == 3)
@@ -0,0 +1,22 @@
package carpet.utils;
import carpet.CarpetServer;
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
import java.lang.System;
/**
* Runs only from GitHub Actions to generate the wiki
* page with all Carpet rules on it.
* It is here so it can be managed by the IDE
*
* @author altrisi
*
*/
public class RulePrinter implements PreLaunchEntrypoint {
@Override
public void onPreLaunch() {
CarpetServer.onGameStarted();
CarpetServer.settingsManager.printAllRulesToLog(null);
System.exit(0);
}
}
@@ -231,8 +231,9 @@ public class SpawnReporter
return SpawnGroup.AMBIENT;
case CYAN:
return SpawnGroup.WATER_AMBIENT;
default:
return null;
}
return null;
}
public static SpawnGroup get_creature_type_from_code(String type_code)