diff --git a/build.gradle.kts b/build.gradle.kts index 7cc7a645..89c6cac2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:master-SNAPSHOT") { + implementation("com.github.SkriptDev:skript-parser:dev~patch-SNAPSHOT") { // TODO (change after release) isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java b/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java index b4571b7c..55d31164 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java @@ -1,10 +1,21 @@ package com.github.skriptdev.skript.api.hytale; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Location; import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; +import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; +import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + /** * Represents a block in a world. * Hytale doesn't appear to have a representation of a block in a world. @@ -18,12 +29,28 @@ public class Block { private @NotNull BlockType type; private final @NotNull Vector3i pos; + public Block(@NotNull World world, @NotNull Vector3i pos) { + this.world = world; + this.pos = pos; + this.type = Objects.requireNonNull(world.getBlockType(pos)); + } + public Block(@NotNull World world, @NotNull Vector3i pos, @NotNull BlockType type) { this.world = world; this.pos = pos; this.type = type; } + public Block(@NotNull Location location) { + World world = Universe.get().getWorld(location.getWorld()); + if (world == null) { + throw new IllegalArgumentException("World '" + location.getWorld() + "' not found."); + } + BlockType blockType = world.getBlockType(location.getPosition().toVector3i()); + assert blockType != null; + this(world, location.getPosition().toVector3i(), blockType); + } + public @NotNull BlockType getType() { return this.type; } @@ -33,6 +60,80 @@ public void setType(@NotNull BlockType type) { this.world.setBlock(this.pos.getX(), this.pos.getY(), this.pos.getZ(), type.getId()); } + public byte getFluidLevel() { + long index = ChunkUtil.indexChunkFromBlock(this.pos.getX(), this.pos.getZ()); + Ref columnRef = this.world.getChunk(index).getReference(); + Store store = columnRef.getStore(); + ChunkColumn column = store.getComponent(columnRef, ChunkColumn.getComponentType()); + Ref section = column.getSection(ChunkUtil.chunkCoordinate(this.pos.getY())); + if (section == null) { + return 0; + } else { + FluidSection fluidSection = store.getComponent(section, FluidSection.getComponentType()); + return fluidSection.getFluidLevel(this.pos.getX(), this.pos.getY(), this.pos.getZ()); + } + } + + public void setFluidLevel(byte level) { + long index = ChunkUtil.indexChunkFromBlock(this.pos.getX(), this.pos.getZ()); + this.world.getChunkAsync(index).thenApply((chunk) -> { + Ref columnRef = chunk.getReference(); + Store store = columnRef.getStore(); + ChunkColumn column = store.getComponent(columnRef, ChunkColumn.getComponentType()); + if (column == null) return null; + + Ref section = column.getSection(ChunkUtil.chunkCoordinate(this.pos.getY())); + if (section == null) { + return null; + } else { + FluidSection fluidSection = store.getComponent(section, FluidSection.getComponentType()); + if (fluidSection == null) { + return null; + } + + + Fluid fluid = fluidSection.getFluid(this.pos.getX(), this.pos.getY(), this.pos.getZ()); + if (fluid == null) return null; + fluidSection.setFluid(this.pos.getX(), this.pos.getY(), this.pos.getZ(), fluid, level); + } + return chunk; + }); + } + + public Fluid getFluid() { + int fluidId = this.world.getFluidId(this.pos.getX(), this.pos.getY(), this.pos.getZ()); + if (fluidId == -1) { + return null; + } + return Fluid.getAssetMap().getAsset(fluidId); + } + + public void setFluid(@NotNull Fluid fluid) { + long index = ChunkUtil.indexChunkFromBlock(this.pos.getX(), this.pos.getZ()); + this.world.getChunkAsync(index).thenApply((chunk) -> { + Ref columnRef = chunk.getReference(); + Store store = columnRef.getStore(); + ChunkColumn column = store.getComponent(columnRef, ChunkColumn.getComponentType()); + if (column == null) return null; + + Ref section = column.getSection(ChunkUtil.chunkCoordinate(this.pos.getY())); + if (section == null) { + return null; + } else { + FluidSection fluidSection = store.getComponent(section, FluidSection.getComponentType()); + if (fluidSection == null) { + return null; + } + + + byte level = fluidSection.getFluidLevel(this.pos.getX(), this.pos.getY(), this.pos.getZ()); + if (level <= 0) level = 8; + fluidSection.setFluid(this.pos.getX(), this.pos.getY(), this.pos.getZ(), fluid, level); + } + return chunk; + }); + } + public void breakBlock() { int setting = 0; // TODO not sure what to actually use here this.world.breakBlock(this.pos.getX(), this.pos.getY(), this.pos.getZ(), setting); @@ -46,6 +147,10 @@ public void breakBlock() { return this.pos; } + public @NotNull Location getLocation() { + return new Location(this.world.getName(), this.pos.getX(), this.pos.getY(), this.pos.getZ()); + } + public String toTypeString() { return String.format("[%s] block at (%s,%s,%s) in '%s'", this.type.getId(), this.pos.getX(), this.pos.getY(), this.pos.getZ(), this.world.getName()); diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/EntityComponentUtils.java b/src/main/java/com/github/skriptdev/skript/api/hytale/EntityComponentUtils.java index 35fdf11a..088a26da 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/EntityComponentUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/EntityComponentUtils.java @@ -1,17 +1,30 @@ package com.github.skriptdev.skript.api.hytale; +import com.hypixel.hytale.component.AddReason; +import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.util.Pair; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Quick utility class for accessing entity components. */ +@SuppressWarnings("UnusedReturnValue") public class EntityComponentUtils { /** @@ -31,4 +44,43 @@ public class EntityComponentUtils { return store.getComponent(reference, EntityStatsModule.get().getEntityStatMapComponentType()); } + /** + * Get the MovementStatesComponent of an entity. + * + * @param entity Entity to get component from + * @return MovementStatesComponent of the entity, or null if not found + */ + public static @Nullable MovementStatesComponent getMovementStatesComponent(Entity entity) { + Ref reference = entity.getReference(); + if (reference == null) return null; + + Store store = reference.getStore(); + return store.getComponent(reference, MovementStatesComponent.getComponentType()); + } + + @SuppressWarnings({"DataFlowIssue", "deprecation"}) + public static @NotNull Pair dropItem(Store store, ItemStack itemStack, Location location, Vector3f velocity, float pickupDelay) { + if (itemStack.isEmpty() || !itemStack.isValid()) { + return new Pair<>(null, null); + } + + Vector3d position = location.getPosition(); + Vector3f rotation = location.getRotation(); + + Holder itemEntityHolder = ItemComponent.generateItemDrop(store, itemStack, position, rotation, velocity.getX(), velocity.getY(), velocity.getZ()); + if (itemEntityHolder == null) { + return new Pair<>(null, null); + } + ; + + ItemComponent itemComponent = itemEntityHolder.getComponent(ItemComponent.getComponentType()); + if (itemComponent != null) { + itemComponent.setPickupDelay(pickupDelay); + } + + store.addEntity(itemEntityHolder, AddReason.SPAWN); + + return new Pair<>(EntityUtils.getEntity(itemEntityHolder), itemComponent); + } + } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/docs/JsonDocPrinter.java b/src/main/java/com/github/skriptdev/skript/api/skript/docs/JsonDocPrinter.java new file mode 100644 index 00000000..50933cb6 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/docs/JsonDocPrinter.java @@ -0,0 +1,477 @@ +package com.github.skriptdev.skript.api.skript.docs; + +import com.github.skriptdev.skript.api.skript.event.CancellableContext; +import com.github.skriptdev.skript.api.utils.Utils; +import com.github.skriptdev.skript.plugin.HySk; +import com.hypixel.hytale.server.core.command.system.CommandSender; +import com.hypixel.hytale.server.core.util.BsonUtil; +import io.github.syst3ms.skriptparser.Parser; +import io.github.syst3ms.skriptparser.docs.Documentation; +import io.github.syst3ms.skriptparser.lang.CodeSection; +import io.github.syst3ms.skriptparser.lang.Effect; +import io.github.syst3ms.skriptparser.lang.Structure; +import io.github.syst3ms.skriptparser.lang.base.ConditionalExpression; +import io.github.syst3ms.skriptparser.lang.base.ExecutableExpression; +import io.github.syst3ms.skriptparser.pattern.PatternElement; +import io.github.syst3ms.skriptparser.registration.ExpressionInfo; +import io.github.syst3ms.skriptparser.registration.SkriptAddon; +import io.github.syst3ms.skriptparser.registration.SkriptRegistration; +import io.github.syst3ms.skriptparser.registration.SyntaxInfo; +import io.github.syst3ms.skriptparser.registration.context.ContextValue; +import io.github.syst3ms.skriptparser.structures.functions.Function; +import io.github.syst3ms.skriptparser.structures.functions.FunctionParameter; +import io.github.syst3ms.skriptparser.structures.functions.Functions; +import io.github.syst3ms.skriptparser.structures.functions.JavaFunction; +import io.github.syst3ms.skriptparser.types.Type; +import io.github.syst3ms.skriptparser.types.TypeManager; +import org.bson.BsonArray; +import org.bson.BsonBoolean; +import org.bson.BsonDocument; +import org.bson.BsonString; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; + +public class JsonDocPrinter { + + private final CommandSender sender; + private final SkriptRegistration registration; + private final SkriptAddon addon; + private final String addonKey; + private final boolean includeSkriptParser; + + public JsonDocPrinter(CommandSender sender, SkriptAddon addon) { + this.sender = sender; + this.registration = addon.getSkriptRegistration(); + this.addon = registration.getRegisterer(); + this.addonKey = this.addon.getAddonName().toLowerCase(Locale.ROOT).replace(" ", "_"); + this.includeSkriptParser = registration instanceof com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; + } + + public void printDocs() { + long start = System.currentTimeMillis(); + String addonName = this.addon.getAddonName(); + Utils.log(this.sender, "Printing documentation for '%s'", addonName); + SkriptRegistration parserRegistration = Parser.getMainRegistration(); + + BsonDocument mainDocs = new BsonDocument(); + + // EVENTS + Utils.log(this.sender, "Printing events"); + printEvents(mainDocs, this.registration); + if (this.includeSkriptParser) { + printEvents(mainDocs, parserRegistration); + } + + // STRUCTURES + Utils.log(this.sender, "Printing structures"); + printStructures(mainDocs, this.registration); + if (this.includeSkriptParser) { + printStructures(mainDocs, parserRegistration); + } + + // EXPRESSIONS + Utils.log(this.sender, "Printing expressions and conditions"); + printExpressions(mainDocs, this.registration); + if (this.includeSkriptParser) { + printExpressions(mainDocs, parserRegistration); + } + + // FUNCTIONS + Utils.log(this.sender, "Printing functions"); + // TODO functions need to be linked to an addon or something + printFunctions(mainDocs, this.registration); + + // EFFECTS + Utils.log(this.sender, "Printing effects"); + printEffects(mainDocs, this.registration); + if (this.includeSkriptParser) { + printEffects(mainDocs, parserRegistration); + } + + // TYPES + Utils.log(this.sender, "Printing types"); + printTypes(mainDocs, this.registration); + if (this.includeSkriptParser) { + printTypes(mainDocs, parserRegistration); + } + + // SECTIONS + Utils.log(this.sender, "Printing sections"); + printSections(mainDocs, this.registration); + if (this.includeSkriptParser) { + printSections(mainDocs, parserRegistration); + } + + Utils.log(this.sender, "Writing documentation to 'docs/%s/docs.json' file", addonName); + File file = getFile(); + BsonUtil.writeDocument(file.toPath(), mainDocs, false); + Utils.log(this.sender, "Done creating docs in %sms!", System.currentTimeMillis() - start); + } + + private void printEvents(BsonDocument mainDocs, SkriptRegistration registration) { + BsonArray eventsArray = mainDocs.getArray("events", new BsonArray()); + + List> contextValues = registration.getContextValues(); + + registration.getEvents().forEach(event -> { + Documentation documentation = event.getDocumentation(); + if (documentation.isNoDoc()) return; + + BsonDocument eventDoc = new BsonDocument(); + + if (!Structure.class.isAssignableFrom(event.getSyntaxClass())) { + printDocumentation("event", eventDoc, event); + } + + AtomicBoolean cancellable = new AtomicBoolean(false); + event.getContexts().forEach(context -> { + if (CancellableContext.class.isAssignableFrom(context)) { + cancellable.set(true); + } + }); + eventDoc.put("cancellable", new BsonBoolean(cancellable.get())); + + List> valuesForThisEvent = new ArrayList<>(); + contextValues.forEach(contextValue -> { + if (event.getContexts().contains(contextValue.getContext())) { + valuesForThisEvent.add(contextValue); + } + }); + BsonArray eventValues = eventDoc.getArray("event values", new BsonArray()); + if (!valuesForThisEvent.isEmpty()) { + valuesForThisEvent.forEach(contextValue -> { + eventValues.add(new BsonString(contextValue.getPattern().toString())); + }); + + } + eventsArray.add(eventDoc); + }); + mainDocs.put("events", eventsArray); + } + + private void printStructures(BsonDocument mainDocs, SkriptRegistration registration) { + BsonArray structuresArray = mainDocs.getArray("structures", new BsonArray()); + + registration.getEvents().forEach(event -> { + Documentation documentation = event.getDocumentation(); + if (documentation.isNoDoc()) return; + + BsonDocument structureDoc = new BsonDocument(); + + if (Structure.class.isAssignableFrom(event.getSyntaxClass())) { + printDocumentation("structure", structureDoc, event); + } + }); + + mainDocs.put("structures", structuresArray); + } + + private void printExpressions(BsonDocument mainDocs, SkriptRegistration registration) { + List>> values = new ArrayList<>(registration.getExpressions().values()); + + BsonArray exprsArray = mainDocs.getArray("expressions", new BsonArray()); + BsonArray condArray = mainDocs.getArray("conditions", new BsonArray()); + + for (List> expressionInfos : values) { + expressionInfos.forEach(expressionInfo -> { + Documentation documentation = expressionInfo.getDocumentation(); + if (documentation.isNoDoc()) return; + + BsonDocument expressionDoc = new BsonDocument(); + printDocumentation("effect-expression", expressionDoc, expressionInfo); + String returnType = expressionInfo.getReturnType().getType().getBaseName(); + expressionDoc.put("return type", new BsonString(returnType)); + + Class syntaxClass = expressionInfo.getSyntaxClass(); + if (ExecutableExpression.class.isAssignableFrom(syntaxClass)) { + // TODO new section on the docs?!?!?! + exprsArray.add(expressionDoc); + } else if (ConditionalExpression.class.isAssignableFrom(syntaxClass)) { + condArray.add(expressionDoc); + } else { + exprsArray.add(expressionDoc); + + } + }); + } + mainDocs.put("expressions", exprsArray); + mainDocs.put("conditions", condArray); + } + + @SuppressWarnings("unchecked") + private void printFunctions(BsonDocument mainDocs, SkriptRegistration registration) { + String addonKey = registration.getRegisterer().getAddonName().toLowerCase(Locale.ROOT).replace(" ", "_"); + BsonArray functionsArray = mainDocs.getArray("functions", new BsonArray()); + Functions.getGlobalFunctions().stream().sorted(Comparator.comparing(Function::getName)).forEach(function -> { + if (function instanceof JavaFunction jf) { + Documentation documentation = jf.getDocumentation(); + if (documentation.isNoDoc()) return; + + BsonDocument functionDoc = new BsonDocument(); + String name = documentation.getName(); + if (name == null) name = function.getName(); + + String id = "function:" + addonKey + ":" + name.toLowerCase(Locale.ROOT).replace(" ", "_"); + + functionDoc.put("name", new BsonString(name)); + functionDoc.put("id", new BsonString(id)); + + // Create params + FunctionParameter[] parameters = jf.getParameters(); + + List parameterNames = new ArrayList<>(); + for (FunctionParameter parameter : parameters) { + Optional> byClass = TypeManager.getByClass(parameter.getType()); + if (byClass.isPresent()) { + Type type = byClass.get(); + String typeName; + if (parameter.isSingle()) { + typeName = type.getBaseName(); + } else { + typeName = type.getPluralForm(); + } + String format = String.format("%s:%s", parameter.getName(), typeName); + parameterNames.add(format); + } + } + + + // Create a pattern for a function + String pattern = String.format("%s(%s)", jf.getName(), String.join(", ", parameterNames)); + functionDoc.put("patterns", new BsonArray(List.of(new BsonString(pattern)))); + + // DESCRIPTION + BsonArray descriptionArray = new BsonArray(); + for (String s : documentation.getDescription()) { + descriptionArray.add(new BsonString(s)); + } + functionDoc.put("description", descriptionArray); + + // USAGE + String usage = documentation.getUsage(); + if (usage != null) { + functionDoc.put("usage", new BsonString(usage)); + } + + // EXAMPLES + String[] examples = documentation.getExamples(); + if (examples != null) { + BsonArray exampleArray = new BsonArray(); + for (String s : examples) { + exampleArray.add(new BsonString(s)); + } + functionDoc.put("examples", exampleArray); + } + + // SINCE + String since = documentation.getSince(); + if (since != null) { + functionDoc.put("since", new BsonArray(List.of(new BsonString(since)))); + } + + // RETURN TYPE + Optional> returnType = (Optional>) jf.getReturnType(); + if (returnType.isPresent()) { + Optional> byClass = TypeManager.getByClass(returnType.get()); + byClass.ifPresent(type -> + functionDoc.put("return type", new BsonString(type.getBaseName()))); + } + functionsArray.add(functionDoc); + + } + }); + mainDocs.put("functions", functionsArray); + } + + private void printEffects(BsonDocument mainDocs, SkriptRegistration registration) { + BsonArray effectsArray = mainDocs.getArray("effects", new BsonArray()); + for (SyntaxInfo effect : registration.getEffects()) { + Documentation documentation = effect.getDocumentation(); + if (documentation.isNoDoc()) continue; + + BsonDocument effectDoc = new BsonDocument(); + printDocumentation("effect", effectDoc, effect); + effectsArray.add(effectDoc); + } + mainDocs.put("effects", effectsArray); + } + + private void printTypes(BsonDocument mainDocs, SkriptRegistration registration) { + BsonArray typesArray = mainDocs.getArray("types", new BsonArray()); + + String addonName = registration.getRegisterer().getAddonName().toLowerCase(Locale.ROOT).replace(" ", "_"); + + registration.getTypes().forEach(type -> { + Documentation documentation = type.getDocumentation(); + if (documentation.isNoDoc()) return; + + BsonDocument syntaxDoc = new BsonDocument(); + String baseName = type.getBaseName(); + String docName = documentation.getName(); + syntaxDoc.put("name", new BsonString(docName != null ? docName : baseName)); + syntaxDoc.put("id", getId("type", baseName)); + + // DESCRIPTION + BsonArray descriptionArray = new BsonArray(); + for (String s : documentation.getDescription()) { + descriptionArray.add(new BsonString(s)); + } + syntaxDoc.put("description", descriptionArray); + + // USAGE + String usage = documentation.getUsage(); + if (usage != null) { + syntaxDoc.put("usage", new BsonString(usage)); + } + + // PATTERNS + BsonArray patternArray = new BsonArray(); + for (String pluralForm : type.getPluralForms()) { + patternArray.add(new BsonString(pluralForm)); + } + syntaxDoc.put("patterns", patternArray); + + // EXAMPLES + String[] examples = documentation.getExamples(); + if (examples != null) { + BsonArray exampleArray = new BsonArray(); + for (String s : examples) { + exampleArray.add(new BsonString(s)); + } + syntaxDoc.put("examples", exampleArray); + } + + // SINCE + String since = documentation.getSince(); + if (since != null) { + syntaxDoc.put("since", new BsonArray(List.of(new BsonString(since)))); + } + + + typesArray.add(syntaxDoc); + + }); + mainDocs.put("types", typesArray); + } + + private void printSections(BsonDocument mainDocs, SkriptRegistration registration) { + BsonArray sectionsArray = mainDocs.getArray("sections", new BsonArray()); + List> sections = new ArrayList<>(registration.getSections()); + sections.sort(Comparator.comparing(info -> info.getSyntaxClass().getSimpleName())); + for (SyntaxInfo section : sections) { + Documentation documentation = section.getDocumentation(); + if (documentation.isNoDoc()) continue; + + BsonDocument sectionDoc = new BsonDocument(); + printDocumentation("section", sectionDoc, section); + sectionsArray.add(sectionDoc); + } + mainDocs.put("sections", sectionsArray); + } + + private void printDocumentation(String type, BsonDocument syntaxDoc, SyntaxInfo syntaxInfo) { + Documentation documentation = syntaxInfo.getDocumentation(); + + // NAME and ID + syntaxDoc.put("name", getName(syntaxInfo)); + syntaxDoc.put("id", getId(type, syntaxInfo)); + + // EXPERIMENTAL + // TODO + + // DESCRIPTION + BsonArray descriptionArray = new BsonArray(); + for (String s : documentation.getDescription()) { + descriptionArray.add(new BsonString(s)); + } + syntaxDoc.put("description", descriptionArray); + + // USAGE + String usage = documentation.getUsage(); + if (usage != null) { + syntaxDoc.put("usage", new BsonString(usage)); + } + + // PATTERNS + List patterns = syntaxInfo.getPatterns(); + if (patterns.isEmpty()) { + BsonArray patternArray = new BsonArray(); + patterns.forEach(pattern -> patternArray.add(new BsonString(pattern.toString()))); + syntaxDoc.put("patterns", patternArray); + } + + // EXAMPLES + String[] examples = documentation.getExamples(); + if (examples != null) { + BsonArray exampleArray = new BsonArray(); + for (String s : examples) { + exampleArray.add(new BsonString(s)); + } + syntaxDoc.put("examples", exampleArray); + } + + // SINCE + String since = documentation.getSince(); + if (since != null) { + syntaxDoc.put("since", new BsonArray(List.of(new BsonString(since)))); + } + } + + private @NotNull File getFile() { + File file = HySk.getInstance().getDataDirectory().resolve("docs/" + this.addonKey + "/docs.json").toFile(); + if (!file.exists()) { + File parentFile = file.getParentFile(); + if (!parentFile.exists()) { + if (!parentFile.mkdirs()) { + throw new RuntimeException("Unable to create docs directory."); + } else { + Utils.log(this.sender, "Created docs directory."); + } + } + try { + if (!file.createNewFile()) { + Utils.error("Failed to create docs.json file!"); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return file; + } + + private BsonString getId(String type, SyntaxInfo syntaxInfo) { + Documentation documentation = syntaxInfo.getDocumentation(); + String name = documentation.getName(); + if (name == null) name = syntaxInfo.getSyntaxClass().getSimpleName(); + return getId(type, name); + } + + private BsonString getName(SyntaxInfo syntaxInfo) { + Documentation documentation = syntaxInfo.getDocumentation(); + String name = documentation.getName(); + if (name == null) { + name = syntaxInfo.getSyntaxClass().getSimpleName(); + } + return new BsonString(name); + } + + private BsonString getId(String type, String syntaxId) { + String addonName = this.addonKey; + if (addonName.equalsIgnoreCase("skript-parser")) { + // So the parser doesn't have its own docs + addonName = "HySkript"; + } + String s = type + ":" + addonName + ":" + syntaxId; + return new BsonString(s.toLowerCase(Locale.ROOT).replace(" ", "_")); + + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/docs/DocPrinter.java b/src/main/java/com/github/skriptdev/skript/api/skript/docs/MarkdownDocPrinter.java similarity index 90% rename from src/main/java/com/github/skriptdev/skript/api/skript/docs/DocPrinter.java rename to src/main/java/com/github/skriptdev/skript/api/skript/docs/MarkdownDocPrinter.java index 1b74af21..a8e27c69 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/docs/DocPrinter.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/docs/MarkdownDocPrinter.java @@ -40,14 +40,14 @@ /** * Documentation printer. */ -public class DocPrinter { +public class MarkdownDocPrinter { /** * Print all docs to file. */ public static void printDocs() { Skript skript = HySk.getInstance().getSkript(); - SkriptRegistration registration = skript.getRegistration(); + SkriptRegistration registration = skript.getSkriptRegistration(); try { Utils.log("Printing documentation"); @@ -278,7 +278,7 @@ private static void printDocumentation(String type, PrintWriter writer, Document writer.println("> Things may not work as expected and may change without notice. "); } String[] description = documentation.getDescription(); - if (description != null) { + if (description.length > 0) { writer.println("- **Description**:"); for (String s : description) { if (s.contains("\n")) { @@ -300,13 +300,45 @@ private static void printDocumentation(String type, PrintWriter writer, Document writer.println("[Click Here](https://github.com/SkriptDev/HySkript/wiki/_usage-" + documentation.getName().replace(" ", "-") + ")"); File usageFile = getFile("_usage-" + documentation.getName()); try { + List values = new ArrayList<>(); + List variations = new ArrayList<>(); + for (String s : usage.split(", ")) { + if (s.startsWith("*")) { + variations.add(s); + } else { + values.add(s); + } + } + boolean hasVariations = !variations.isEmpty(); PrintWriter usageWriter = new PrintWriter(usageFile, StandardCharsets.UTF_8); usageWriter.println("# Usage: " + documentation.getName()); + + usageWriter.println("
"); + usageWriter.println("Values"); + usageWriter.println(); usageWriter.println("```yaml"); // Yaml makes it a nicer blue - for (String s : usage.split(", ")) { + for (String s : values.stream().sorted().toList()) { usageWriter.println(s); } usageWriter.println("```"); + usageWriter.println(); + usageWriter.println("
"); + usageWriter.println(); + + if (hasVariations) { + usageWriter.println("
"); + usageWriter.println("Variations"); + usageWriter.println(); + usageWriter.println("```yaml"); // Yaml makes it a nicer blue + for (String s : variations.stream().sorted().toList()) { + usageWriter.println(s); + } + usageWriter.println("```"); + usageWriter.println(); + usageWriter.println("
"); + usageWriter.println(); + } + usageWriter.close(); } catch (IOException e) { throw new RuntimeException(e); @@ -328,10 +360,11 @@ private static void printDocumentation(String type, PrintWriter writer, Document }); } } - if (documentation.getExamples() != null) { + String[] examples = documentation.getExamples(); + if (examples.length > 0) { writer.println("- **Examples**: "); writer.println("```applescript"); - for (String s : documentation.getExamples()) { + for (String s : examples) { writer.println(s); } writer.println("```"); @@ -342,7 +375,7 @@ private static void printDocumentation(String type, PrintWriter writer, Document } static private @NotNull File getFile(String name) { - File file = HySk.getInstance().getDataDirectory().resolve("docs/" + name + ".md").toFile(); + File file = HySk.getInstance().getDataDirectory().resolve("docs/markdown-docs/" + name + ".md").toFile(); if (!file.exists()) { File parentFile = file.getParentFile(); if (!parentFile.exists()) { diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/variables/JsonVariableStorage.java b/src/main/java/com/github/skriptdev/skript/api/skript/variables/JsonVariableStorage.java index 0dce1760..3897edc4 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/variables/JsonVariableStorage.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/variables/JsonVariableStorage.java @@ -4,6 +4,7 @@ import com.github.skriptdev.skript.plugin.HySk; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.util.BsonUtil; import io.github.syst3ms.skriptparser.config.Config.ConfigSection; @@ -13,7 +14,11 @@ import io.github.syst3ms.skriptparser.variables.Variables; import org.bson.BsonBinaryReader; import org.bson.BsonBinaryWriter; +import org.bson.BsonBoolean; import org.bson.BsonDocument; +import org.bson.BsonDouble; +import org.bson.BsonInt32; +import org.bson.BsonInt64; import org.bson.BsonString; import org.bson.BsonValue; import org.bson.codecs.BsonDocumentCodec; @@ -89,7 +94,6 @@ private void startFileWatcher() { if (this.changes.get() >= this.changesToSave) { try { saveVariables(false); - this.logger.info("Saved " + this.changes.get() + " changes to '" + this.file.getName() + "'"); // TODO REMOVE (debug) this.changes.set(0); } catch (IOException e) { this.logger.error("Failed to save variable file", ErrorType.EXCEPTION); @@ -118,6 +122,10 @@ private void loadVariablesFromFile() { JsonObject value = entry.getValue().getAsJsonObject(); String type = value.get("type").getAsString(); JsonElement jsonValue = value.get("value"); + if (jsonValue == null) { + this.logger.error("Skipping variable '" + name + "' due to missing value", ErrorType.STRUCTURE_ERROR); + return; + } this.logger.debug("Loading variable '" + name + "' of type '" + type + "' from file. With data '" + jsonValue.toString() + "'"); loadVariable(name, type, jsonValue); @@ -166,6 +174,23 @@ protected boolean save(@NotNull String name, @Nullable String type, @Nullable Js if (bsonValue instanceof BsonDocument doc) { myDocument.put("value", doc); + } else if (value instanceof JsonPrimitive primitive) { + // Bson translate doesn't handle primitives + if (primitive.isNumber()) { + switch (primitive.getAsNumber()) { + case Long l -> myDocument.put("value", new BsonInt64(l)); + case Integer i -> myDocument.put("value", new BsonInt32(i)); + case Short s -> myDocument.put("value", new BsonInt32(s.intValue())); + case Byte b -> myDocument.put("value", new BsonInt32(b.intValue())); + case Double d -> myDocument.put("value", new BsonDouble(d)); + case Float f -> myDocument.put("value", new BsonDouble(f)); + case null, default -> myDocument.put("value", bsonValue); + } + } else if (primitive.isBoolean()) { + myDocument.put("value", new BsonBoolean(primitive.getAsBoolean())); + } else if (primitive.isString()) { + myDocument.put("value", new BsonString(primitive.getAsString())); + } } else { myDocument.put("value", bsonValue); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/variables/SerializerUtils.java b/src/main/java/com/github/skriptdev/skript/api/skript/variables/SerializerUtils.java new file mode 100644 index 00000000..55f9a3cc --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/variables/SerializerUtils.java @@ -0,0 +1,40 @@ +package com.github.skriptdev.skript.api.skript.variables; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.ExtraInfo; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; +import org.bson.BsonDocument; +import org.jetbrains.annotations.NotNull; + +/** + * Utilities for serializers. + */ +public class SerializerUtils { + + /** + * Create a serializer for a {@link Codec Codec} + * + * @param codec Codec to create serializer for + * @param Type of the codec + * @return Codec serializer + */ + public static TypeSerializer getCodecSerializer(BuilderCodec codec) { + return new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull T value) { + BsonDocument encode = codec.encode(value, new ExtraInfo()); + String json = encode.toJson(); + return gson.fromJson(json, JsonElement.class); + } + + @Override + public T deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + return codec.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); + } + }; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java index fb9d4ef3..c452c608 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java @@ -6,6 +6,7 @@ import com.github.skriptdev.skript.api.skript.variables.JsonVariableStorage; import com.github.skriptdev.skript.api.utils.ReflectionUtils; import com.github.skriptdev.skript.api.utils.Utils; +import com.github.skriptdev.skript.plugin.command.EffectCommands; import com.github.skriptdev.skript.plugin.elements.ElementRegistration; import io.github.syst3ms.skriptparser.Parser; import io.github.syst3ms.skriptparser.config.Config; @@ -38,6 +39,7 @@ public class Skript extends SkriptAddon { private ScriptsLoader scriptsLoader; Skript(HySk hySk) { + super("HySkript"); INSTANCE = this; this.hySk = hySk; this.scriptsPath = hySk.getDataDirectory().resolve("scripts"); @@ -53,14 +55,24 @@ public class Skript extends SkriptAddon { for (LogEntry logEntry : this.logger.close()) { Utils.log(null, logEntry); } + Utils.log("HySkript setup complete!"); } private void setup() { ReflectionUtils.init(); ArgUtils.init(); this.registration = new SkriptRegistration(this); - this.elementRegistration = new ElementRegistration(this); + this.elementRegistration = new ElementRegistration(this.registration); this.elementRegistration.registerElements(); + ConfigSection effectCommandSection = this.skriptConfig.getConfigSection("effect-commands"); + if (effectCommandSection != null) { + if (effectCommandSection.getBoolean("enabled")) { + EffectCommands.register(this, + effectCommandSection.getString("token"), + effectCommandSection.getBoolean("allow-ops"), + effectCommandSection.getString("required-permission")); + } + } // FINALIZE SETUP this.registration.register(); @@ -170,7 +182,7 @@ public SkriptLogger getLogger() { * * @return The Skript registration. */ - public @NotNull SkriptRegistration getRegistration() { + public @NotNull io.github.syst3ms.skriptparser.registration.SkriptRegistration getSkriptRegistration() { return this.registration; } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/command/EffectCommands.java b/src/main/java/com/github/skriptdev/skript/plugin/command/EffectCommands.java new file mode 100644 index 00000000..b94c2b77 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/command/EffectCommands.java @@ -0,0 +1,109 @@ +package com.github.skriptdev.skript.plugin.command; + +import com.github.skriptdev.skript.api.utils.Utils; +import com.github.skriptdev.skript.plugin.Skript; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.event.events.player.PlayerChatEvent; +import com.hypixel.hytale.server.core.permissions.PermissionsModule; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.Effect; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.log.LogEntry; +import io.github.syst3ms.skriptparser.log.SkriptLogger; +import io.github.syst3ms.skriptparser.parsing.ParserState; +import io.github.syst3ms.skriptparser.parsing.SyntaxParser; +import io.github.syst3ms.skriptparser.registration.context.ContextValue.Usage; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +public class EffectCommands { + + public static void register(Skript skript, String token, boolean allowOps, String permission) { + skript.getSkriptRegistration().newContextValue(PlayerEffectContext.class, Player.class, true, "player", PlayerEffectContext::getPlayer) + .setUsage(Usage.EXPRESSION_OR_ALONE) + .register(); + skript.getSkriptRegistration().newContextValue(PlayerEffectContext.class, Player.class, true, "me", PlayerEffectContext::getPlayer) + .setUsage(Usage.EXPRESSION_OR_ALONE) + .register(); + + skript.getPlugin().getEventRegistry().registerGlobal(PlayerChatEvent.class, event -> { + if (event.getContent().startsWith(token)) { + PlayerRef sender = event.getSender(); + + // PERM CHECK + PermissionsModule perm = PermissionsModule.get(); + Set groupsForUser = perm.getGroupsForUser(sender.getUuid()); + if (!allowOps) { + if (groupsForUser.contains("OP") && !perm.hasPermission(sender.getUuid(), permission)) { + return; + } + } else if (!perm.hasPermission(sender.getUuid(), permission)) { + return; + } + + event.setCancelled(true); + + ParserState parserState = new ParserState(); + parserState.setCurrentContexts(Set.of(PlayerEffectContext.class)); + SkriptLogger skriptLogger = new SkriptLogger(true); + + String effectString = event.getContent().substring(1); + Optional optionalEffect = SyntaxParser.parseEffect(effectString, parserState, skriptLogger); + + if (optionalEffect.isEmpty()) { + skriptLogger.finalizeLogs(); + for (LogEntry logEntry : skriptLogger.close()) { + Utils.log(sender, logEntry); + } + return; + } + + Effect effect = optionalEffect.get(); + Ref reference = sender.getReference(); + if (reference == null) return; + + UUID worldUuid = sender.getWorldUuid(); + if (worldUuid == null) return; + + World world = Universe.get().getWorld(worldUuid); + if (world == null) return; + + skriptLogger.info("Executing: '" + effectString + "'"); + skriptLogger.finalizeLogs(); + for (LogEntry logEntry : skriptLogger.close()) { + Utils.log(sender, logEntry); + } + + if (world.isInThread()) { + Player player = world.getEntityStore().getStore().getComponent(reference, Player.getComponentType()); + effect.walk(new PlayerEffectContext(player)); + } else { + world.execute(() -> { + Player player = world.getEntityStore().getStore().getComponent(reference, Player.getComponentType()); + effect.walk(new PlayerEffectContext(player)); + }); + } + + } + }); + } + + private record PlayerEffectContext(Player player) implements TriggerContext { + + public Player[] getPlayer() { + return new Player[]{this.player}; + } + + @Override + public String getName() { + return "player effect context"; + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java index 4f7b39d3..1e50b08d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.plugin.command; -import com.github.skriptdev.skript.api.skript.docs.DocPrinter; +import com.github.skriptdev.skript.api.skript.docs.JsonDocPrinter; +import com.github.skriptdev.skript.api.skript.docs.MarkdownDocPrinter; import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.HySk; import com.github.skriptdev.skript.plugin.Skript; @@ -9,15 +10,20 @@ import com.hypixel.hytale.server.core.command.system.AbstractCommand; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.CommandRegistry; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; +import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; import com.hypixel.hytale.server.core.receiver.IMessageReceiver; +import io.github.syst3ms.skriptparser.registration.SkriptAddon; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; +import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; /** * Main command for HySkript. @@ -33,7 +39,7 @@ public SkriptCommand(CommandRegistry registry) { addAliases("sk"); // Keep these in alphabetical order - addSubCommand(docsCommand()); + addSubCommand(new DocsCommand()); addSubCommand(infoCommand()); addSubCommand(new ReloadCommand()); @@ -45,7 +51,7 @@ private static class ReloadCommand extends AbstractCommand { private final RequiredArg stringRequiredArg; protected ReloadCommand() { - super("reload", "Reloads all scripts."); + super("reload", "Reloads scripts."); this.stringRequiredArg = withRequiredArg("script", "A script to reload", ArgTypes.STRING); addSubCommand(new AbstractCommand("all", "Reloads all scripts.") { @Override @@ -69,13 +75,59 @@ protected CompletableFuture execute(@NotNull CommandContext commandContext } } - private AbstractCommand docsCommand() { - return new AbstractCommand("docs", "Print docs to file.") { - @Override - protected CompletableFuture execute(@NotNull CommandContext commandContext) { - return CompletableFuture.runAsync(DocPrinter::printDocs); + private static class DocsCommand extends AbstractCommandCollection { + protected DocsCommand() { + super("docs", "Print docs to file."); + addSubCommand(mdDocsSubCommand()); + addSubCommand(new JsonDocsCommand()); + } + + private AbstractCommand mdDocsSubCommand() { + AbstractCommand abstractCommand = new AbstractCommand("markdown", "Print Markdown docs to file.") { + @Override + protected CompletableFuture execute(@NotNull CommandContext commandContext) { + return CompletableFuture.runAsync(MarkdownDocPrinter::printDocs); + } + }; + abstractCommand.addAliases("md"); + return abstractCommand; + } + } + + private static class JsonDocsCommand extends AbstractCommand { + + OptionalArg addonArg; + FlagArg all; + + protected JsonDocsCommand() { + super("json", "Print JSON docs to file."); + this.addonArg = withOptionalArg("addon", "An addon to print docs for.", ArgTypes.STRING); + this.all = withFlagArg("all", "Print docs for all addons."); + } + + @Override + protected @Nullable CompletableFuture execute(@NotNull CommandContext commandContext) { + if (this.all.provided(commandContext)) { + return CompletableFuture.runAsync(() -> { + for (SkriptAddon addon : SkriptAddon.getAddons().stream().filter(addon -> !addon.getAddonName().equalsIgnoreCase("skript-parser")).toList()) { + JsonDocPrinter jsonDocPrinter = new JsonDocPrinter(commandContext.sender(), addon); + jsonDocPrinter.printDocs(); + } + }); } - }; + SkriptAddon addon = HySk.getInstance().getSkript(); + if (this.addonArg.provided(commandContext)) { + String s = this.addonArg.get(commandContext); + SkriptAddon skriptAddon = SkriptAddon.getAddon(s); + if (s.equalsIgnoreCase("skript-parser") || skriptAddon == null) { + Utils.log(commandContext.sender(), Level.SEVERE, "Addon '%s' not found.", s); + return CompletableFuture.completedFuture(null); + } + addon = skriptAddon; + } + JsonDocPrinter jsonDocPrinter = new JsonDocPrinter(commandContext.sender(), addon); + return CompletableFuture.runAsync(jsonDocPrinter::printDocs); + } } private AbstractCommand infoCommand() { @@ -89,9 +141,19 @@ protected CompletableFuture execute(@NotNull CommandContext commandContext private void printInfo(IMessageReceiver sender) { Utils.sendMessage(sender, "HySkript Version: %s", HySk.getInstance().getManifest().getVersion()); - Utils.sendMessage(sender, "Hytale Version: %s", ManifestUtil.getImplementationVersion()); + Utils.sendMessage(sender, "Hytale Version: %s (%s)", ManifestUtil.getImplementationVersion(), ManifestUtil.getPatchline()); Utils.sendMessage(sender, "Java Version: %s", System.getProperty("java.version")); + List addons = SkriptAddon.getAddons().stream() + .filter(addon -> !addon.getAddonName().equalsIgnoreCase("skript-parser") + && !addon.getAddonName().equalsIgnoreCase("HySkript")) + .toList(); + + if (!addons.isEmpty()) { + Utils.sendMessage(sender, "Loaded Addons: %s"); + addons.forEach(addon -> Utils.sendMessage(sender, " - %s", addon.getAddonName())); + } + Message link = Message.raw("https://github.com/SkriptDev/HySkript") .link("https://github.com/SkriptDev/HySkript") .color("#0CE8C3"); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java index a01b2cfb..5bb6c578 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java @@ -1,9 +1,10 @@ package com.github.skriptdev.skript.plugin.elements; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; -import com.github.skriptdev.skript.plugin.Skript; import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; import com.github.skriptdev.skript.plugin.elements.command.ScriptSubCommand; +import com.github.skriptdev.skript.plugin.elements.types.DefaultComparators; +import com.github.skriptdev.skript.plugin.elements.types.DefaultConverters; import com.github.skriptdev.skript.plugin.elements.conditions.ConditionHandler; import com.github.skriptdev.skript.plugin.elements.effects.EffectHandler; import com.github.skriptdev.skript.plugin.elements.events.EventHandler; @@ -17,8 +18,8 @@ public class ElementRegistration { private final SkriptRegistration registration; - public ElementRegistration(Skript skript) { - this.registration = skript.getRegistration(); + public ElementRegistration(SkriptRegistration registration) { + this.registration = registration; } public void registerElements() { @@ -27,6 +28,10 @@ public void registerElements() { // TYPES Types.register(this.registration); + // COMPARATORS/CONVERTERS + DefaultComparators.register(); + DefaultConverters.register(); + // CONDITIONS ConditionHandler.register(this.registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondHasPermission.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondHasPermission.java index 3c4a0d5d..65473708 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondHasPermission.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondHasPermission.java @@ -10,13 +10,16 @@ import io.github.syst3ms.skriptparser.registration.SkriptRegistration; import org.jetbrains.annotations.NotNull; +import java.util.UUID; + public class CondHasPermission extends ConditionalExpression { public static void register(SkriptRegistration registration) { registration.newExpression(CondHasPermission.class, Boolean.class, true, - "%players/playerrefs% (has|have) permission %strings%") + "%players/playerrefs/uuid% (has|have) permission %strings%", + "%players/playerrefs/uuid% (don't|do not|doesn't|does not) (has|have) permission %strings%") .name("Permission") - .description("Checks if the specified player(s) have the specified permission.") + .description("Checks if the specified players/playerrefs/uuids have/don't have the specified permissions.") .examples("if player has permission \"hytale.admin\":") .since("1.0.0") .register(); @@ -30,23 +33,26 @@ public static void register(SkriptRegistration registration) { public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { this.players = expressions[0]; this.permission = (Expression) expressions[1]; + setNegated(matchedPattern == 1); return true; } - @Override public boolean check(@NotNull TriggerContext ctx) { + PermissionsModule permissionsModule = PermissionsModule.get(); this.permission.check(ctx, string -> { this.players.check(ctx, p -> { if (p instanceof Player player) { return player.hasPermission(string); } else if (p instanceof PlayerRef playerRef) { - return PermissionsModule.get().hasPermission(playerRef.getUuid(), string); + return permissionsModule.hasPermission(playerRef.getUuid(), string); + } else if (p instanceof UUID uuid) { + return permissionsModule.hasPermission(uuid, string); } return false; - }); + }, isNegated()); return false; - }); + }, isNegated()); return false; } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondInventoryCanHold.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondInventoryCanHold.java new file mode 100644 index 00000000..453ce2a4 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondInventoryCanHold.java @@ -0,0 +1,58 @@ +package com.github.skriptdev.skript.plugin.elements.conditions; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.base.ConditionalExpression; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +public class CondInventoryCanHold extends ConditionalExpression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(CondInventoryCanHold.class, Boolean.class, true, + "%inventory/itemcontainer% can hold %itemstacks%", + "%inventory/itemcontainer% (can't|cannot) hold %itemstacks%") + .name("Inventory Can Hold") + .description("Checks if the inventory can hold the given items.") + .examples("if inventory of player can hold itemstack of ingredient_poop:", + "if inventory of player can hold {_itemstack}:") + .since("INSERT VERSION") + .register(); + } + + private Expression holders; + private Expression items; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.holders = expressions[0]; + this.items = (Expression) expressions[1]; + setNegated(matchedPattern == 1); + return true; + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return this.holders.check(ctx, holder -> + this.items.check(ctx, item -> { + if (holder instanceof Inventory inventory) { + return inventory.getCombinedEverything().canAddItemStack(item); + } else if (holder instanceof ItemContainer container) { + return container.canAddItemStack(item); + } + return false; + }), isNegated()); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String hold = isNegated() ? "cannot hold" : "can hold"; + return this.holders.toString(ctx, debug) + " " + hold + " " + this.items.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondPlayerIsCrouching.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondPlayerIsCrouching.java new file mode 100644 index 00000000..c8bc7702 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondPlayerIsCrouching.java @@ -0,0 +1,54 @@ +package com.github.skriptdev.skript.plugin.elements.conditions; + +import com.github.skriptdev.skript.api.hytale.EntityComponentUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.base.ConditionalExpression; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +public class CondPlayerIsCrouching extends ConditionalExpression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(CondPlayerIsCrouching.class, Boolean.class, true, + "%players% (is|are)[neg:( not|n't)] crouching", + "%players% (isn't|is not|aren't|are not) crouching") + .name("Player is Crouching") + .description("Checks if the player is crouching.") + .examples("if player is crouching:", + "\tmessage \"You are crouching!\"") + .since("INSERT VERSION") + .register(); + } + + private Expression players; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + setNegated(matchedPattern == 1); + this.players = (Expression) expressions[0]; + return true; + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return this.players.check(ctx, player -> { + MovementStatesComponent component = EntityComponentUtils.getMovementStatesComponent(player); + if (component == null) return false; + + return component.getMovementStates().crouching; + }, isNegated()); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + boolean single = this.players.isSingle(); + String s = isNegated() ? (single ? "isn't" : "aren't") : (single ? "is" : "are"); + return this.players.toString(ctx, debug) + " " + s + " crouching"; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java index 66082fab..2404495f 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java @@ -1,11 +1,14 @@ package com.github.skriptdev.skript.plugin.elements.conditions; -import io.github.syst3ms.skriptparser.registration.SkriptRegistration; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; public class ConditionHandler { public static void register(SkriptRegistration registration) { CondHasPermission.register(registration); + CondInventoryCanHold.register(registration); + CondPlayerIsCrouching.register(registration); } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffDropItem.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffDropItem.java new file mode 100644 index 00000000..c1f7ee43 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffDropItem.java @@ -0,0 +1,128 @@ +package com.github.skriptdev.skript.plugin.elements.effects; + +import com.github.skriptdev.skript.api.hytale.EntityComponentUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.Effect; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class EffDropItem extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffDropItem.class, + "drop %items/itemstacks% at %location%", + "drop %items/itemstacks% at %location% with pickup delay %duration%", + "drop %items/itemstacks% at %location% with velocity %vector3f%", + "drop %items/itemstacks% at %location% with velocity %vector3f% [and] [with] pickup delay %duration%") + .name("Drop Item") + .description("Drops the specified items.") + .examples("drop ingredient_poop at location of player", + "drop {_itemstack} at location of player with pickup delay 10 seconds", + "drop {_i} at location of player with velocity vector3f(0,1,0) and with pickup delay 5 seconds") + .since("INSERT VERSION") + .register(); + } + + private Expression items; + private Expression location; + private Expression pickupDelay; + private Expression velocity; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.items = expressions[0]; + this.location = (Expression) expressions[1]; + if (matchedPattern == 1) { + this.pickupDelay = (Expression) expressions[2]; + } else if (matchedPattern == 2) { + this.velocity = (Expression) expressions[2]; + } else if (matchedPattern == 3) { + this.velocity = (Expression) expressions[2]; + this.pickupDelay = (Expression) expressions[3]; + } + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + Location location = this.location.getSingle(ctx).orElse(null); + if (location == null) return; + + String worldName = location.getWorld(); + World world = Universe.get().getWorld(worldName); + if (world == null) return; + + Store store = world.getEntityStore().getStore(); + + List itemStacks = new ArrayList<>(); + for (Object o : this.items.getArray(ctx)) { + if (o instanceof ItemStack itemStack) { + itemStacks.add(itemStack); + } else if (o instanceof Item item) { + ItemStack itemStack = new ItemStack(item.getId()); + itemStacks.add(itemStack); + } + } + + Vector3f velocity; + if (this.velocity != null) { + Optional single = this.velocity.getSingle(ctx); + if (single.isPresent()) { + velocity = single.get(); + } else { + velocity = Vector3f.ZERO; + } + } else { + velocity = Vector3f.ZERO; + } + + float pickupDelay; + if (this.pickupDelay != null) { + Optional single = this.pickupDelay.getSingle(ctx); + if (single.isPresent()) { + Duration duration = single.get(); + pickupDelay = duration.toMillis() / 1000.0f; + } else { + pickupDelay = 0; + } + } else { + pickupDelay = 0f; + } + + if (world.isInThread()) { + world.execute(() -> { + for (ItemStack itemStack : itemStacks) { + EntityComponentUtils.dropItem(store, itemStack, location, velocity, pickupDelay); + } + }); + } else { + for (ItemStack itemStack : itemStacks) { + EntityComponentUtils.dropItem(store, itemStack, location, velocity, pickupDelay); + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String velocity = this.velocity != null ? " with velocity " + this.velocity.toString(ctx, debug) : ""; + String pickupDelay = this.pickupDelay != null ? " with pickup delay " + this.pickupDelay.toString(ctx, debug) : ""; + return "drop " + this.items.toString(ctx, debug) + " at " + this.location.toString(ctx, debug) + velocity + pickupDelay; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffectHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffectHandler.java index 6f04af64..5554c5a9 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffectHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffectHandler.java @@ -9,6 +9,7 @@ public static void register(SkriptRegistration registration) { EffBroadcast.register(registration); EffCancelEvent.register(registration); EffDelay.register(registration); + EffDropItem.register(registration); EffKick.register(registration); EffKill.register(registration); EffSendMessage.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDeath.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDeath.java index bd469d4a..9ba2c1b8 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDeath.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDeath.java @@ -6,6 +6,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; @@ -23,6 +24,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; + public class EvtEntityDeath extends SkriptEvent { public static void register(SkriptRegistration registration) { @@ -38,7 +41,7 @@ public static void register(SkriptRegistration registration) { "\tbroadcast \"Poor %context-victim%\" has died!", "", "on death of player:", - "\tset {lost::%uuid of context-victim%} to context-lost-items") + "\tset {lost::%uuid of context-victim%} to context-lost-itemstacks") .since("1.0.0") .register(); @@ -53,7 +56,9 @@ public static void register(SkriptRegistration registration) { registration.addContextValue(EntityDeathContext.class, Damage.class, true, "death-info", EntityDeathContext::getDamage); registration.addContextValue(EntityDeathContext.class, - ItemStack.class, false, "lost-items", EntityDeathContext::getItemsLostOnDeath); + Item.class, false, "lost-items", EntityDeathContext::getItemsLostOnDeath); + registration.addContextValue(EntityDeathContext.class, + ItemStack.class, false, "lost-itemstacks", EntityDeathContext::getItemStacksLostOnDeath); } private static EntityDeathListener LISTENER; @@ -147,7 +152,11 @@ public Damage[] getDamage() { return new Damage[]{this.component.getDeathInfo()}; } - public ItemStack[] getItemsLostOnDeath() { + public Item[] getItemsLostOnDeath() { + return Arrays.stream(this.component.getItemsLostOnDeath()).map(ItemStack::getItem).toArray(Item[]::new); + } + + public ItemStack[] getItemStacksLostOnDeath() { return this.component.getItemsLostOnDeath(); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerBreakBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerBreakBlock.java index 1dc71581..c57aae16 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerBreakBlock.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerBreakBlock.java @@ -12,6 +12,7 @@ import com.hypixel.hytale.component.system.EntityEventSystem; import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent; import com.hypixel.hytale.server.core.inventory.ItemStack; @@ -42,7 +43,8 @@ public static void register(SkriptRegistration reg) { reg.addContextValue(BreakBlockEventContext.class, Block.class, true, "block", BreakBlockEventContext::getBlock); reg.addContextValue(BreakBlockEventContext.class, World.class, true, "world", BreakBlockEventContext::getWorld); reg.addContextValue(BreakBlockEventContext.class, BlockType.class, true, "blocktype", BreakBlockEventContext::getBlockType); - reg.addContextValue(BreakBlockEventContext.class, ItemStack.class, true, "item-in-hand", BreakBlockEventContext::getItemInHand); + reg.addContextValue(BreakBlockEventContext.class, Item.class, true, "item-in-hand", BreakBlockEventContext::getItemInHand); + reg.addContextValue(BreakBlockEventContext.class, ItemStack.class, true, "itemstack-in-hand", BreakBlockEventContext::getItemStackInHand); } private static BlockBreakEventSystem SYSTEM; @@ -90,7 +92,13 @@ private BlockType[] getBlockType() { return new BlockType[]{this.event.getBlockType()}; } - private ItemStack[] getItemInHand() { + private Item[] getItemInHand() { + ItemStack itemInHand = this.event.getItemInHand(); + if (itemInHand == null) return null; + return new Item[]{itemInHand.getItem()}; + } + + private ItemStack[] getItemStackInHand() { return new ItemStack[]{this.event.getItemInHand()}; } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItem.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItem.java index 1f17ed38..57dcf7ec 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItem.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItem.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.ecs.DropItemEvent; import com.hypixel.hytale.server.core.event.events.ecs.DropItemEvent.Drop; @@ -37,6 +38,7 @@ public static void register(SkriptRegistration reg) { reg.addContextValue(DropItemContext.class, Player.class, true, "player", DropItemContext::getPlayer); reg.addContextValue(DropItemContext.class, Float.class, true, "throw-speed", DropItemContext::getThrowSpeed); + reg.addContextValue(DropItemContext.class, Item.class, true, "dropped-item", DropItemContext::getItem); reg.addContextValue(DropItemContext.class, ItemStack.class, true, "dropped-itemstack", DropItemContext::getItemStack); } @@ -71,6 +73,10 @@ public Float[] getThrowSpeed() { return new Float[]{this.drop.getThrowSpeed()}; } + public Item[] getItem() { + return new Item[]{this.drop.getItemStack().getItem()}; + } + public ItemStack[] getItemStack() { return new ItemStack[]{this.drop.getItemStack()}; } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItemRequest.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItemRequest.java index 4f3510d3..276d91ab 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItemRequest.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItemRequest.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.ecs.DropItemEvent; import com.hypixel.hytale.server.core.inventory.Inventory; @@ -39,6 +40,7 @@ public static void register(SkriptRegistration reg) { reg.addContextValue(RequestDropItemContext.class, Player.class, true, "player", RequestDropItemContext::getPlayer); reg.addContextValue(RequestDropItemContext.class, Integer.class, true, "slot-id", RequestDropItemContext::getSlotId); reg.addContextValue(RequestDropItemContext.class, Integer.class, true, "inventory-section-id", RequestDropItemContext::getInventorySectionId); + reg.addContextValue(RequestDropItemContext.class, Item.class, true, "item", RequestDropItemContext::getItem); reg.addContextValue(RequestDropItemContext.class, ItemStack.class, true, "itemstack", RequestDropItemContext::getItemStack); } @@ -78,6 +80,16 @@ public Integer[] getInventorySectionId() { return new Integer[]{(int) this.request.getInventorySectionId()}; } + public Item[] getItem() { + Inventory inventory = this.player.getInventory(); + ItemContainer container = inventory.getSectionById(this.request.getInventorySectionId()); + if (container == null) return null; + ItemStack itemStack = container.getItemStack(this.request.getSlotId()); + if (itemStack == null) return null; + Item item = itemStack.getItem(); + return new Item[]{item}; + } + public ItemStack[] getItemStack() { Inventory inventory = this.player.getInventory(); ItemContainer container = inventory.getSectionById(this.request.getInventorySectionId()); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseClick.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseClick.java index 89327882..e74738b6 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseClick.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseClick.java @@ -25,7 +25,8 @@ public class EvtPlayerMouseClick extends SkriptEvent { public static void register(SkriptRegistration reg) { reg.newEvent(EvtPlayerMouseClick.class, "player mouse button", "player mouse click") .name("Player Mouse Button") - .description("Called when a player clicks on their mouse.") + .description("Called when a player clicks on their mouse.", + "**NOTE**: This event appears to be broken internally and doesn't seem to call") .since("1.0.0") .setHandledContexts(MouseClickContext.class) .register(); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseMove.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseMove.java index 2d0e4b3e..ba02bc88 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseMove.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseMove.java @@ -23,7 +23,8 @@ public class EvtPlayerMouseMove extends SkriptEvent { public static void register(SkriptRegistration reg) { reg.newEvent(EvtPlayerMouseClick.class, "player mouse motion", "player mouse move") .name("Player Mouse Motion") - .description("Called when a player moves their mouse.") + .description("Called when a player moves their mouse.", + "**NOTE**: This event appears to be broken internally and doesn't seem to call") .since("1.0.0") .setHandledContexts(MouseMoveContext.class) .register(); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index b63c14e2..f6e3cb79 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -2,15 +2,22 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockAt; -import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockType; +import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockFluid; +import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockTypeAtLocation; +import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockTypeOfBlock; +import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockFluidLevel; +import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprTargetBlockOfPlayer; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityHealth; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityStat; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprNPCType; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprName; +import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprTargetEntityOfEntity; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventory; +import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventorySlot; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemContainer; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStack; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemType; +import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemsInInventory; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprClassInfoOf; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationDirection; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationOf; @@ -35,18 +42,31 @@ public class ExpressionHandler { public static void register(SkriptRegistration registration) { - ExprAllPlayers.register(registration); + // BLOCK ExprBlockAt.register(registration); - ExprBlockType.register(registration); - ExprChatMessage.register(registration); - ExprClassInfoOf.register(registration); - ExprConsole.register(registration); + ExprBlockFluid.register(registration); + ExprBlockFluidLevel.register(registration); + ExprBlockTypeAtLocation.register(registration); + ExprBlockTypeOfBlock.register(registration); + ExprTargetBlockOfPlayer.register(registration); + + // ENTITY ExprEntityHealth.register(registration); ExprEntityStat.register(registration); + ExprName.register(registration); + ExprNPCType.register(registration); + ExprTargetEntityOfEntity.register(registration); + + // ITEM ExprInventory.register(registration); + ExprInventorySlot.register(registration); ExprItemContainer.register(registration); + ExprItemsInInventory.register(registration); ExprItemStack.register(registration); ExprItemType.register(registration); + + // OTHER + ExprClassInfoOf.register(registration); ExprLocationDirection.register(registration); ExprLocationOf.register(registration); ExprMessage.register(registration); @@ -54,17 +74,25 @@ public static void register(SkriptRegistration registration) { ExprMessageLink.register(registration); ExprMessageParam.register(registration); ExprMessageProperties.register(registration); - ExprName.register(registration); - ExprNPCType.register(registration); - ExprPlayerSpawns.register(registration); ExprUUID.register(registration); ExprUUIDRandom.register(registration); ExprVector3d.register(registration); ExprVector3f.register(registration); ExprVector3i.register(registration); + + // PLAYER + ExprAllPlayers.register(registration); + ExprChatMessage.register(registration); + ExprPlayerSpawns.register(registration); + + // SERVER + ExprConsole.register(registration); + + // WORLD ExprWorld.register(registration); ExprWorldOf.register(registration); ExprWorldSpawn.register(registration); + } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockFluid.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockFluid.java new file mode 100644 index 00000000..2fc395f6 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockFluid.java @@ -0,0 +1,86 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.block; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ExprBlockFluid implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprBlockFluid.class, Fluid.class, true, + "fluid (at|of) %locations/blocks%") + .name("Block Fluid") + .description("Get the fluid at a location/block.") + .examples("set {_fluid} to fluid at player's location", + "set fluid of block at player's location to slime_red") + .since("INSERT VERSION") + .register(); + } + + private Expression locations; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.locations = expressions[0]; + return true; + } + + @Override + public Fluid[] getValues(@NotNull TriggerContext ctx) { + List fluids = new ArrayList<>(); + + for (Object o : this.locations.getArray(ctx)) { + if (o instanceof Block block) { + fluids.add(block.getFluid()); + } else if (o instanceof Location location) { + Block block = new Block(location); + fluids.add(block.getFluid()); + } + } + + return fluids.toArray(Fluid[]::new); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) return Optional.of(new Class[]{Fluid.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null) return; + + if (!(changeWith[0] instanceof Fluid fluid)) return; + for (Object o : this.locations.getArray(ctx)) { + if (o instanceof Block block) { + block.setFluid(fluid); + } else if (o instanceof Location location) { + Block block = new Block(location); + block.setFluid(fluid); + } + } + } + + @Override + public boolean isSingle() { + return this.locations.isSingle(); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "fluid of " + this.locations.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockFluidLevel.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockFluidLevel.java new file mode 100644 index 00000000..63a7c486 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockFluidLevel.java @@ -0,0 +1,79 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.block; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.math.vector.Location; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ExprBlockFluidLevel implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprBlockFluidLevel.class, Number.class, true, + "[block] fluid level of %locations/blocks%") + .name("Block Fluid Level") + .description("Get.set the fluid level of a block.") + .examples("set fluid level of block at player's location to 8") + .since("INSERT VERSION") + .register(); + } + + private Expression locations; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.locations = expressions[0]; + return true; + } + + @Override + public Number[] getValues(@NotNull TriggerContext ctx) { + List levels = new ArrayList<>(); + + for (Object o : this.locations.getArray(ctx)) { + if (o instanceof Block block) { + levels.add(block.getFluidLevel()); + } else if (o instanceof Location location) { + Block block = new Block(location); + levels.add(block.getFluidLevel()); + } + } + + return levels.toArray(Number[]::new); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) return Optional.of(new Class[]{Number.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null) return; + if (!(changeWith[0] instanceof Number level)) return; + + for (Object o : this.locations.getArray(ctx)) { + if (o instanceof Block block) { + block.setFluidLevel(level.byteValue()); + } else if (o instanceof Location location) { + Block block = new Block(location); + block.setFluidLevel(level.byteValue()); + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "fluid level of " + this.locations.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockType.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeAtLocation.java similarity index 61% rename from src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockType.java rename to src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeAtLocation.java index 3a23b227..4aacd143 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockType.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeAtLocation.java @@ -9,18 +9,22 @@ import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.parsing.ParseContext; import io.github.syst3ms.skriptparser.registration.SkriptRegistration; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; import org.jetbrains.annotations.NotNull; -public class ExprBlockType implements Expression { +import java.util.Optional; + +public class ExprBlockTypeAtLocation implements Expression { public static void register(SkriptRegistration registration) { - registration.newExpression(ExprBlockType.class, BlockType.class, true, + registration.newExpression(ExprBlockTypeAtLocation.class, BlockType.class, true, "block[ ]type at %vector3i% in %world%", "block[ ]type at %location%") - .name("Block Type") - .description("Returns the BlockType at a given location in a world.") + .name("Block Type at Location") + .description("Get/set the BlockType at a given location in a world.") .examples("set {_block} to blocktype at location of player", "set {_block} to blocktype at vector3i(1, 2, 3) in world of player", + "set blocktype at location of player to empty # This is how Hytale refers to air", "if blocktype at vector3i(1,1,1) = rock_stone_brick:") .since("1.0.0") .register(); @@ -61,6 +65,37 @@ public BlockType[] getValues(@NotNull TriggerContext ctx) { return null; } + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) return Optional.of(new Class[]{BlockType.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeMode != ChangeMode.SET) return; + // Even though changeWith is NotNull... it still can be null + if (changeWith == null) return; + + if (!(changeWith[0] instanceof BlockType type)) return; + + if (this.loc != null) { + Location location = this.loc.getSingle(ctx).orElse(null); + if (location == null) return; + World world = Universe.get().getWorld(location.getWorld()); + Vector3i vector3i = location.getPosition().toVector3i(); + if (world != null) + world.setBlock(vector3i.getX(), vector3i.getY(), vector3i.getZ(), type.getId()); + } else if (this.pos != null && this.world != null) { + Vector3i pos = this.pos.getSingle(ctx).orElse(null); + World world = this.world.getSingle(ctx).orElse(null); + if (pos == null || world == null) return; + + world.setBlock(pos.getX(), pos.getY(), pos.getZ(), type.getId()); + } + } + @Override public String toString(@NotNull TriggerContext ctx, boolean debug) { if (this.loc != null) { diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeOfBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeOfBlock.java new file mode 100644 index 00000000..90b07a42 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeOfBlock.java @@ -0,0 +1,80 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.block; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ExprBlockTypeOfBlock implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprBlockTypeOfBlock.class, BlockType.class, false, + "block[ ]type of %blocks%") + .name("Block Type of Block") + .description("Get/set the BlockType of a block.") + .examples("on player block break:", + "\tif context-blocktype = ore_copper_stone:", + "\t\tcancel event", + "\t\tset blocktype of context-block to rock_Stone", + "", + "set blocktype of {_block} to empty # This is how Hytale refers to air") + .since("INSERT VERSION") + .register(); + } + + private Expression blocks; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.blocks = (Expression) expressions[0]; + return true; + } + + @Override + public BlockType[] getValues(@NotNull TriggerContext ctx) { + List blockTypes = new ArrayList<>(); + for (Block block : this.blocks.getArray(ctx)) { + blockTypes.add(block.getType()); + } + return blockTypes.toArray(BlockType[]::new); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) return Optional.of(new Class[]{BlockType.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeMode != ChangeMode.SET) return; + if (changeWith == null) return; + + if (!(changeWith[0] instanceof BlockType type)) return; + + for (Block block : this.blocks.getArray(ctx)) { + block.setType(type); + } + } + + @Override + public boolean isSingle() { + return this.blocks.isSingle(); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "blocktype of " + this.blocks.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprTargetBlockOfPlayer.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprTargetBlockOfPlayer.java new file mode 100644 index 00000000..e1f1a9d1 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprTargetBlockOfPlayer.java @@ -0,0 +1,63 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.block; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.core.util.TargetUtil; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class ExprTargetBlockOfPlayer implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprTargetBlockOfPlayer.class, Block.class, true, + "target block of %player%") + .name("Target Block of Player") + .description("Returns the block the player is looking at.") + .examples("set {_block} to target block of player") + .since("INSERT VERSION") + .register(); + } + + private Expression player; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.player = (Expression) expressions[0]; + return true; + } + + @Override + public Block[] getValues(@NotNull TriggerContext ctx) { + Optional single = this.player.getSingle(ctx); + if (single.isEmpty()) return null; + + Player player = single.get(); + Ref ref = player.getReference(); + World world = player.getWorld(); + if (world == null || ref == null) return null; + + Store store = world.getEntityStore().getStore(); + + // TODO configurable maxDistance + Vector3i targetBlock = TargetUtil.getTargetBlock(ref, 50, store); + if (targetBlock == null) return null; + return new Block[]{new Block(world, targetBlock)}; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "target block of " + this.player.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprTargetEntityOfEntity.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprTargetEntityOfEntity.java new file mode 100644 index 00000000..fb6fda46 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprTargetEntityOfEntity.java @@ -0,0 +1,65 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.entity; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.entity.EntityUtils; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.core.util.TargetUtil; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class ExprTargetEntityOfEntity implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprTargetEntityOfEntity.class, Entity.class, true, + "target entity of %entity%") + .name("Target Entity of Entity") + .description("Returns the target entity of the given entity.") + .examples("set {_target} to target entity of player") + .since("INSERT VERSION") + .register(); + } + + private Expression entity; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.entity = (Expression) expressions[0]; + return true; + } + + @SuppressWarnings("deprecation") + @Override + public Entity[] getValues(@NotNull TriggerContext ctx) { + Optional single = this.entity.getSingle(ctx); + if (single.isEmpty()) return null; + + Entity entity = single.get(); + Ref ref = entity.getReference(); + World world = entity.getWorld(); + if (world == null || ref == null) return null; + + Store store = world.getEntityStore().getStore(); + Ref targetEntity = TargetUtil.getTargetEntity(ref, store); + if (targetEntity == null || !targetEntity.isValid()) return null; + + // TODO better handling of this deprecation + Entity target = EntityUtils.getEntity(targetEntity, store); + + return new Entity[]{target}; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "target entity of " + this.entity.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventorySlot.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventorySlot.java new file mode 100644 index 00000000..dd1156d9 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventorySlot.java @@ -0,0 +1,101 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.item; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class ExprInventorySlot implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprInventorySlot.class, ItemStack.class, true, + "itemstack in slot %number% of %inventory/itemcontainer%") + .name("Itemstack in Inventory Slot") + .description("Get/set/delete the itemstack in a slot of an Inventory or ItemContainer.", + "When using Inventory, this will combine all ItemContainers in the inventory and grab from there.") + .examples("set {_item} to itemstack in slot 3 of inventory of player", + "delete itemstack in slot 3 of inventory of player", + "delete itemstack in slot 2 of hotbar item container of inventory of player", + "set itemstack in slot 1 of hotbar item container of inventory of player to itemstack of ingredient_poop") + .since("INSERT VERSION") + .register(); + } + + private Expression slot; + private Expression container; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.slot = (Expression) expressions[0]; + this.container = expressions[1]; + return true; + } + + @Override + public ItemStack[] getValues(@NotNull TriggerContext ctx) { + Number number = this.slot.getSingle(ctx).orElse(null); + Object o = this.container.getSingle(ctx).orElse(null); + if (o == null || number == null) return null; + int i = number.intValue(); + + ItemStack itemStack = null; + if (o instanceof Inventory inventory) { + itemStack = inventory.getCombinedEverything().getItemStack(number.shortValue()); + } else if (o instanceof ItemContainer itemContainer) { + itemStack = itemContainer.getItemStack(number.shortValue()); + } + if (itemStack == null) return null; + + return new ItemStack[]{itemStack}; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) return Optional.of(new Class[]{ItemStack.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeMode == ChangeMode.SET && changeWith == null) return; + Number numSlot = this.slot.getSingle(ctx).orElse(null); + Object o = this.container.getSingle(ctx).orElse(null); + if (numSlot == null || o == null) return; + + if (changeMode == ChangeMode.SET) { + ItemStack itemStack = (ItemStack) changeWith[0]; + if (o instanceof Inventory inventory) { + inventory.getCombinedEverything().setItemStackForSlot(numSlot.shortValue(), itemStack); + } else if (o instanceof ItemContainer itemContainer) { + itemContainer.setItemStackForSlot(numSlot.shortValue(), itemStack); + } + } else if (changeMode == ChangeMode.DELETE) { + if (o instanceof Inventory inventory) { + inventory.getCombinedEverything().setItemStackForSlot(numSlot.shortValue(), null); + } else if (o instanceof ItemContainer itemContainer) { + itemContainer.setItemStackForSlot(numSlot.shortValue(), null); + } + } + + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "itemstack in slot" + this.slot.toString(ctx, debug) + " of " + this.container.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemsInInventory.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemsInInventory.java new file mode 100644 index 00000000..30a0178b --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemsInInventory.java @@ -0,0 +1,63 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.item; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.Statement; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ExprItemsInInventory implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprItemsInInventory.class, ItemStack.class, false, + "itemstacks in %inventory/itemcontainer%") + .name("Itemstacks in Inventory/ItemContainer") + .description("Returns all itemstacks in an Inventory or ItemContainer.") + .examples("set {_items::*} to itemstacks in inventory of player", + "loop itemstacks in inventory of player:") + .since("INSERT VERSION") + .register(); + } + + private Expression container; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.container = expressions[0]; + return true; + } + + + @Override + public ItemStack[] getValues(@NotNull TriggerContext ctx) { + List itemStacks = new ArrayList<>(); + + Optional single = this.container.getSingle(ctx); + if (single.isEmpty()) return null; + + Object o = single.get(); + if (o instanceof Inventory inventory) { + CombinedItemContainer combinedEverything = inventory.getCombinedEverything(); + combinedEverything.forEach((_, itemStack) -> itemStacks.add(itemStack)); + } else if (o instanceof ItemContainer itemContainer) { + itemContainer.forEach((_, itemStack) -> itemStacks.add(itemStack)); + } + + return itemStacks.toArray(new ItemStack[0]); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "itemstacks in " + this.container.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprClassInfoOf.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprClassInfoOf.java index 9e6345b1..00e22874 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprClassInfoOf.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprClassInfoOf.java @@ -4,29 +4,44 @@ import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.parsing.ParseContext; import io.github.syst3ms.skriptparser.registration.SkriptRegistration; +import io.github.syst3ms.skriptparser.types.Type; +import io.github.syst3ms.skriptparser.types.TypeManager; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; public class ExprClassInfoOf implements Expression { public static void register(SkriptRegistration registration) { - registration.addExpression(ExprClassInfoOf.class, String.class, - "class[ ]info of %objects%"); + registration.newExpression(ExprClassInfoOf.class, String.class, false, + "class[ ]info of %objects%") + .name("Class Info") + .description("Returns the name of the class/type of the given object.") + .since("1.0.0") + .register(); } private Expression object; @Override - public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { this.object = expressions[0]; return true; } - @Override - public String[] getValues(TriggerContext ctx) { + public String[] getValues(@NotNull TriggerContext ctx) { Object[] array = this.object.getArray(ctx); String[] strings = new String[array.length]; for (int i = 0; i < array.length; i++) { - strings[i] = array[i].getClass().getName(); + Optional> byClass = TypeManager.getByClass(array[i].getClass()); + if (byClass.isEmpty()) { + strings[i] = "Class: " + array[i].getClass().getSimpleName(); + continue; + } + Type type = byClass.get(); + String name = type.getDocumentation().getName(); + strings[i] = name != null ? name : type.getBaseName(); } return strings; } @@ -37,7 +52,7 @@ public boolean isSingle() { } @Override - public String toString(TriggerContext ctx, boolean debug) { + public String toString(@NotNull TriggerContext ctx, boolean debug) { return "classinfo of " + this.object.toString(ctx, debug); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationOf.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationOf.java index 8d3c588a..f45e8dc8 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationOf.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationOf.java @@ -1,31 +1,66 @@ package com.github.skriptdev.skript.plugin.elements.expressions.other; +import com.github.skriptdev.skript.api.hytale.Block; import com.hypixel.hytale.math.vector.Location; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; -import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; import io.github.syst3ms.skriptparser.registration.SkriptRegistration; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; -public class ExprLocationOf extends PropertyExpression { +import java.util.ArrayList; +import java.util.List; + +public class ExprLocationOf implements Expression { public static void register(SkriptRegistration registration) { - registration.newPropertyExpression(ExprLocationOf.class, Location.class, - "location", "entities") - .name("Location of Entity") - .description("Returns the location of an entity.") + registration.newExpression(ExprLocationOf.class, Location.class, false, + "location[s] of %blocks/entities%", + "%entities/blocks%'[s] location[s]") + .name("Location of Block/Entity") + .description("Returns the location of a block or entity.") .examples("set {_loc} to location of context-player") .since("1.0.0") .register(); } + private Expression objects; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.objects = expressions[0]; + return true; + } + + @SuppressWarnings("removal") + @Override + public Location[] getValues(@NotNull TriggerContext ctx) { + List locations = new ArrayList<>(); + for (Object o : this.objects.getArray(ctx)) { + if (o instanceof Entity entity) { + World world = entity.getWorld(); + assert world != null; + TransformComponent transform = entity.getTransformComponent(); + locations.add(new Location(world.getName(), transform.getPosition())); + } else if (o instanceof Block block) { + locations.add(block.getLocation()); + } + } + return locations.toArray(new Location[0]); + } + + @Override + public boolean isSingle() { + return this.objects.isSingle(); + } + @Override - public @Nullable Location getProperty(Entity entity) { - World world = entity.getWorld(); - assert world != null; - TransformComponent transform = entity.getTransformComponent(); - return new Location(world.getName(), transform.getTransform()); + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String plural = this.objects.isSingle() ? "" : "s"; + return "location" + plural + " of " + this.objects.toString(ctx, debug); } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecDropItem.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecDropItem.java new file mode 100644 index 00000000..35ca7b75 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecDropItem.java @@ -0,0 +1,173 @@ +package com.github.skriptdev.skript.plugin.elements.sections; + +import com.github.skriptdev.skript.api.hytale.EntityComponentUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.CodeSection; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.Statement; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.parsing.ParserState; +import io.github.syst3ms.skriptparser.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; + +public class SecDropItem extends CodeSection { + + public static void register(SkriptRegistration reg) { + reg.newSection(SecDropItem.class, "drop %item/itemstack% at %location%", + "drop %item/itemstack% at %location% with pickup delay %duration%", + "drop %item/itemstack% at %location% with velocity %vector3f%", + "drop %item/itemstack% at %location% with velocity %vector3f% [and] [with] pickup delay %duration%") + .name("Drop Item") + .description("Drops the specified items.") + .examples("drop ingredient_poop at location of player", + "drop {_itemstack} at location of player with pickup delay 10 seconds", + "drop {_i} at location of player with velocity vector3f(0,1,0) and with pickup delay 5 seconds") + .since("INSERT VERSION") + .register(); + + reg.addContextValue(ItemComponentContext.class, ItemComponent.class, true, "item-component", ItemComponentContext::getItemComponent); + reg.addContextValue(ItemComponentContext.class, Entity.class, true, "item-entity", ItemComponentContext::getEntity); + } + + private Expression items; + private Expression location; + private Expression pickupDelay; + private Expression velocity; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.items = expressions[0]; + this.location = (Expression) expressions[1]; + if (matchedPattern == 1) { + this.pickupDelay = (Expression) expressions[2]; + } else if (matchedPattern == 2) { + this.velocity = (Expression) expressions[2]; + } else if (matchedPattern == 3) { + this.velocity = (Expression) expressions[2]; + this.pickupDelay = (Expression) expressions[3]; + } + ParserState parserState = parseContext.getParserState(); + List> triggerContexts = new ArrayList<>(parserState.getCurrentContexts().stream().toList()); + triggerContexts.add(ItemComponentContext.class); + parserState.setCurrentContexts(new HashSet<>(triggerContexts)); + return true; + } + + @Override + public Optional walk(@NotNull TriggerContext ctx) { + Optional next = getNext(); + Location location = this.location.getSingle(ctx).orElse(null); + if (location == null) return next; + + String worldName = location.getWorld(); + World world = Universe.get().getWorld(worldName); + if (world == null) return next; + + Store store = world.getEntityStore().getStore(); + + Optional single1 = this.items.getSingle(ctx); + if (single1.isEmpty()) return next; + Object o = single1.get(); + + ItemStack itemStack; + if (o instanceof ItemStack itemStack1) { + itemStack = itemStack1; + } else if (o instanceof Item item) { + itemStack = new ItemStack(item.getId()); + } else { + return next; + } + + Vector3f velocity; + if (this.velocity != null) { + Optional single = this.velocity.getSingle(ctx); + if (single.isPresent()) { + velocity = single.get(); + } else { + velocity = Vector3f.ZERO; + } + } else { + velocity = Vector3f.ZERO; + } + + float pickupDelay; + if (this.pickupDelay != null) { + Optional single = this.pickupDelay.getSingle(ctx); + if (single.isPresent()) { + Duration duration = single.get(); + pickupDelay = duration.toMillis() / 1000.0f; + } else { + pickupDelay = 0; + } + } else { + pickupDelay = 0f; + } + + Optional first = getFirst(); + if (world.isInThread()) { + world.execute(() -> { + Pair pair = EntityComponentUtils.dropItem(store, itemStack, location, velocity, pickupDelay); + first.ifPresent(statement -> { + Statement.runAll(statement, new ItemComponentContext(pair.getFirst(), pair.getSecond())); + }); + }); + } else { + Pair pair = EntityComponentUtils.dropItem(store, itemStack, location, velocity, pickupDelay); + first.ifPresent(statement -> { + Statement.runAll(statement, new ItemComponentContext(pair.getFirst(), pair.getSecond())); + }); + } + return next; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String velocity = this.velocity != null ? " with velocity " + this.velocity.toString(ctx, debug) : ""; + String pickupDelay = this.pickupDelay != null ? " with pickup delay " + this.pickupDelay.toString(ctx, debug) : ""; + return "drop " + this.items.toString(ctx, debug) + " at " + this.location.toString(ctx, debug) + velocity + pickupDelay; + } + + public static class ItemComponentContext implements TriggerContext { + + private final @Nullable Entity entity; + private final ItemComponent component; + + public ItemComponentContext(@Nullable Entity entity, ItemComponent component) { + this.entity = entity; + this.component = component; + } + + public ItemComponent[] getItemComponent() { + return new ItemComponent[]{this.component}; + } + + public Entity[] getEntity() { + return new Entity[]{this.entity}; + } + + @Override + public String getName() { + return "item component context"; + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SectionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SectionHandler.java index a08a807a..f3f0541f 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SectionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SectionHandler.java @@ -5,6 +5,7 @@ public class SectionHandler { public static void register(SkriptRegistration registration) { + SecDropItem.register(registration); SecExecuteInWorld.register(registration); SecSpawnNPC.register(registration); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultComparators.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultComparators.java new file mode 100644 index 00000000..e706a234 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultComparators.java @@ -0,0 +1,95 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import io.github.syst3ms.skriptparser.types.comparisons.Comparator; +import io.github.syst3ms.skriptparser.types.comparisons.Comparators; +import io.github.syst3ms.skriptparser.types.comparisons.Relation; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.atomic.AtomicReference; + +public class DefaultComparators { + + public static void register() { + block(); + inventory(); + } + + private static void block() { + Comparators.registerComparator(BlockType.class, BlockType.class, new Comparator<>(false) { + @Override + public Relation apply(@NotNull BlockType blockType1, @NotNull BlockType blockType2) { + return Relation.get(blockType1.getId().equals(blockType2.getId())); + } + }); + } + + private static void inventory() { + // Inventory contains ItemStack + Comparators.registerComparator(Inventory.class, ItemStack.class, new Comparator<>(false) { + @Override + public Relation apply(@NotNull Inventory inventory, @NotNull ItemStack itemStack) { + AtomicReference relation = new AtomicReference<>(Relation.NOT_EQUAL); + inventory.getCombinedEverything().forEach((s, is) -> { + if (is.isEquivalentType(itemStack)) { + relation.set(Relation.EQUAL); + } + }); + return relation.get(); + } + }); + // Inventory contains Item + Comparators.registerComparator(Inventory.class, Item.class, new Comparator<>(false) { + @Override + public Relation apply(@NotNull Inventory inventory, @NotNull Item item) { + AtomicReference relation = new AtomicReference<>(Relation.NOT_EQUAL); + inventory.getCombinedEverything().forEach((s, is) -> { + if (is.getItem().equals(item)) { + relation.set(Relation.EQUAL); + } + }); + return relation.get(); + } + }); + + // ItemContainer contains ItemStack + Comparators.registerComparator(ItemContainer.class, ItemStack.class, new Comparator<>(false) { + @Override + public Relation apply(@NotNull ItemContainer container, @NotNull ItemStack itemStack) { + AtomicReference relation = new AtomicReference<>(Relation.NOT_EQUAL); + container.forEach((s, is) -> { + if (is.isEquivalentType(itemStack)) { + relation.set(Relation.EQUAL); + } + }); + return relation.get(); + } + }); + // ItemContainer contains Item + Comparators.registerComparator(ItemContainer.class, Item.class, new Comparator<>(false) { + @Override + public Relation apply(@NotNull ItemContainer container, @NotNull Item item) { + AtomicReference relation = new AtomicReference<>(Relation.NOT_EQUAL); + container.forEach((s, is) -> { + if (is.getItem().equals(item)) { + relation.set(Relation.EQUAL); + } + }); + return relation.get(); + } + }); + + // Compare Item types + Comparators.registerComparator(Item.class, Item.class, new Comparator<>(false) { + @Override + public Relation apply(@NotNull Item item1, @NotNull Item item2) { + return Relation.get(item1.getId().equals(item2.getId())); + } + }); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultConverters.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultConverters.java new file mode 100644 index 00000000..7eb503eb --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultConverters.java @@ -0,0 +1,35 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import io.github.syst3ms.skriptparser.types.conversions.Converters; + +import java.util.Optional; + +public class DefaultConverters { + + public static void register() { + inventory(); + } + + private static void inventory() { + // Item to BlockType + Converters.registerConverter(Item.class, BlockType.class, (item) -> { + if (item.hasBlockType()) { + String blockId = item.getBlockId(); + BlockType asset = BlockType.getAssetMap().getAsset(blockId); + if (asset != null) return Optional.of(asset); + return Optional.empty(); + } + return Optional.empty(); + }); + + // BlockType to Item + Converters.registerConverter(BlockType.class, Item.class, (blockType) -> { + Item item = blockType.getItem(); + if (item != null) return Optional.of(item); + return Optional.empty(); + }); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/Types.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/Types.java index 342d41d9..34926e0d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/Types.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/Types.java @@ -1,497 +1,23 @@ package com.github.skriptdev.skript.plugin.elements.types; -import com.github.skriptdev.skript.api.hytale.Block; -import com.github.skriptdev.skript.api.hytale.Direction; -import com.github.skriptdev.skript.api.skript.command.ArgUtils; -import com.github.skriptdev.skript.api.skript.registration.AssetStoreRegistry; -import com.github.skriptdev.skript.api.skript.registration.EnumRegistry; -import com.github.skriptdev.skript.api.skript.registration.NPCRegistry; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.api.utils.Utils; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.hypixel.hytale.assetstore.AssetRegistry; -import com.hypixel.hytale.builtin.hytalegenerator.assets.biomes.BiomeAsset; -import com.hypixel.hytale.codec.ExtraInfo; -import com.hypixel.hytale.common.util.java.ManifestUtil; -import com.hypixel.hytale.math.vector.Location; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.protocol.InventoryActionType; -import com.hypixel.hytale.server.core.HytaleServer; -import com.hypixel.hytale.server.core.Message; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; -import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; -import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; -import com.hypixel.hytale.server.core.asset.type.item.config.BlockGroup; -import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; -import com.hypixel.hytale.server.core.asset.type.item.config.Item; -import com.hypixel.hytale.server.core.asset.type.item.config.ResourceType; -import com.hypixel.hytale.server.core.asset.type.projectile.config.Projectile; -import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; -import com.hypixel.hytale.server.core.asset.type.weather.config.Weather; -import com.hypixel.hytale.server.core.command.system.CommandSender; -import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType; -import com.hypixel.hytale.server.core.entity.Entity; -import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; -import com.hypixel.hytale.server.core.inventory.ItemStack; -import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; -import com.hypixel.hytale.server.core.inventory.container.ItemContainer; -import com.hypixel.hytale.server.core.modules.entity.damage.Damage; -import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; -import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; -import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; -import com.hypixel.hytale.server.core.receiver.IMessageReceiver; -import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.npc.entities.NPCEntity; import io.github.syst3ms.skriptparser.types.TypeManager; -import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; -import org.bson.BsonDocument; -import org.bson.BsonString; -import org.bson.BsonValue; -import org.jetbrains.annotations.NotNull; -import java.util.UUID; - -@SuppressWarnings("deprecation") public class Types { public static void register(SkriptRegistration registration) { Utils.log("Setting up Types"); - registerJavaTypes(registration); - registerCustomTypes(registration); - registerServerTypes(registration); - registerEntityTypes(registration); - registerItemTypes(registration); - registerBlockTypes(registration); - registerWorldTypes(registration); - registerAssetStoreTypes(registration); + TypesJava.register(registration); + TypesCustom.register(registration); + TypesServer.register(registration); + TypesEntity.register(registration); + TypesItem.register(registration); + TypesBlock.register(registration); + TypesWorld.register(registration); + TypesAssetStore.register(registration); TypeManager.register(registration); } - private static void registerJavaTypes(SkriptRegistration registration) { - registration.newType(UUID.class, "uuid", "uuid@s") - .name("UUID") - .description("Represents a UUID.") - .examples("set {_uuid} to uuid of {_player}") - .since("1.0.0") - .toStringFunction(UUID::toString) - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull UUID value) { - return gson.toJsonTree(value.toString()); - } - - @Override - public UUID deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - return UUID.fromString(element.getAsString()); - } - }) - .register(); - } - - private static void registerCustomTypes(SkriptRegistration reg) { - reg.newType(Direction.class, "direction", "direction@s") - .name("Direction") - .description("Represents a direction in the world.") - .usage(Direction.getUsageString()) - .literalParser(Direction::parse) - .toStringFunction(Direction::getName) - .since("1.0.0") - .register(); - } - - private static void registerServerTypes(SkriptRegistration registration) { - registration.newType(ArgumentType.class, "argumenttype", "argumentType@s") - .name("Argument Type") - .description("Represents the types of arguments that can be used in commands.") - .usage(ArgUtils.getTypeUsage()) - .since("1.0.0") - .register(); - registration.newType(CommandSender.class, "commandsender", "commandSender@s") - .name("Command Sender") - .description("Represents a command sender such as a player or the console.") - .since("1.0.0") - .toStringFunction(CommandSender::getDisplayName) - .register(); - registration.newType(Damage.class, "damage", "damage@s") - .name("Damage") - .description("Represents information about damage dealt to an entity.") - .since("1.0.0") - .register(); - registration.newType(Damage.Source.class, "damagesource", "damageSource@s") - .name("Damage Source") - .description("Represents the source of damage when an entity is damaged/killed.") - .since("1.0.0") - .register(); - registration.newType(IMessageReceiver.class, "messagereceiver", "messageReceiver@s") - .name("Message Receiver") - .description("Represents a receiver of messages such as a player or the console.") - .since("1.0.0") - .register(); - registration.newType(Message.class, "message", "message@s") - .name("Message") - .description("Represents a stylized message sent to a message receiver.") - .since("1.0.0") - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull Message value) { - BsonValue encode = Message.CODEC.encode(value, new ExtraInfo()); - return gson.fromJson(encode.asDocument().toJson(), JsonElement.class); - } - - @Override - public Message deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - BsonDocument decode = BsonDocument.parse(element.toString()); - return Message.CODEC.decode(decode, new ExtraInfo()); - } - }) - .register(); - registration.newType(HytaleServer.class, "server", "server@s") - .name("Server") - .description("Represents the Hytale server.") - .since("1.0.0") - .register(); - registration.newType(Vector3f.class, "vector3f", "vector3f@s") - .name("Vector3f") - .description("Represents a vector in 3D space using floats.", - "Often used for the rotation of entities in a world.") - .since("1.0.0") - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull Vector3f value) { - BsonDocument encode = Vector3f.CODEC.encode(value, new ExtraInfo()); - return gson.fromJson(encode.toJson(), JsonElement.class); - } - - @Override - public Vector3f deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - BsonDocument decode = BsonDocument.parse(element.toString()); - return Vector3f.CODEC.decode(decode, new ExtraInfo()); - } - }) - .register(); - registration.newType(Vector3d.class, "vector3d", "vector3d@s") - .name("Vector3d") - .description("Represents a vector in 3D space using doubles.", - "Often used for the position of entities in a world.") - .since("1.0.0") - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull Vector3d value) { - BsonDocument encode = Vector3d.CODEC.encode(value, new ExtraInfo()); - return gson.fromJson(encode.toJson(), JsonElement.class); - } - - @Override - public Vector3d deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - BsonDocument decode = BsonDocument.parse(element.toString()); - return Vector3d.CODEC.decode(decode, new ExtraInfo()); - } - }) - .register(); - registration.newType(Vector3i.class, "vector3i", "vector3i@s") - .name("Vector3i") - .description("Represents a vector in 3D space using integers.", - "Often used for the position of blocks in a world.") - .since("1.0.0") - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull Vector3i value) { - BsonDocument encode = Vector3i.CODEC.encode(value, new ExtraInfo()); - return gson.fromJson(encode.toJson(), JsonElement.class); - } - - @Override - public Vector3i deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - BsonDocument decode = BsonDocument.parse(element.toString()); - return Vector3i.CODEC.decode(decode, new ExtraInfo()); - } - }) - .register(); - registration.newType(Location.class, "location", "location@s") - .name("Location") - .description("Represents a location in a world.", - "A location contains a world, a position (vector3d) and a rotation (vector3f).") - .since("1.0.0") - .register(); - } - - private static void registerEntityTypes(SkriptRegistration registration) { - registration.newType(NPCRegistry.NPCRole.class, "npcrole", "npcrole@s") - .name("NPC Role") - .description("Represents the type of NPCs in the game.") - .examples("coming soon") // TODO - .usage(NPCRegistry.getTypeUsage()) - .since("1.0.0") - .toStringFunction(NPCRegistry.NPCRole::name) - .literalParser(NPCRegistry::parse) - .register(); - registration.newType(Entity.class, "entity", "entit@y@ies") - .toStringFunction(Entity::toString) // TODO get its name or something - .name("Entity") - .description("Represents any entity in the game, including players and mobs.") - .since("1.0.0") - .register(); - registration.newType(LivingEntity.class, "livingentity", "livingEntit@y@ies") - .name("Living Entity") - .description("Represents any living entity in the game, including players and mobs.") - .since("1.0.0") - .toStringFunction(LivingEntity::getLegacyDisplayName) - .register(); - registration.newType(NPCEntity.class, "npcentity", "npcEntit@y@ies") - .name("NPC Entity") - .description("Represents an NPC entity in the game.") - .since("1.0.0") - .toStringFunction(NPCRegistry::stringify) - .register(); - registration.newType(Player.class, "player", "player@s") - .name("Player") - .description("Represents a player in the game.") - .since("1.0.0") - .toStringFunction(Player::getDisplayName) - .register(); - registration.newType(PlayerRef.class, "playerref", "playerRef@s") - .name("Player Ref") - .description("Represents a reference to a player in the game.") - .since("1.0.0") - .toStringFunction(PlayerRef::getUsername) - .register(); - } - - private static void registerItemTypes(SkriptRegistration registration) { - registration.newType(ItemContainer.class, "itemcontainer", "itemContainer@s") - .name("Item Container") - .description("Represents an item container within an inventory (such as the armor container).") - .since("1.0.0") - .experimental() - .toStringFunction(ItemContainer::toString) - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull ItemContainer value) { - BsonDocument document; - if (value instanceof CombinedItemContainer cic) { - document = CombinedItemContainer.CODEC.encode(cic, new ExtraInfo()).asDocument(); - document.put("type", new BsonString("combined")); - } else { - document = ItemContainer.CODEC.encode(value, new ExtraInfo()).asDocument(); - document.put("type", new BsonString("container")); - } - - return gson.fromJson(document.toJson(), JsonElement.class); - } - - @Override - public ItemContainer deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - BsonDocument parse = BsonDocument.parse(element.toString()); - String type = parse.getString("type").getValue(); - if (type == null) return null; - - if (type.equals("combined")) { - return CombinedItemContainer.CODEC.decode(parse, new ExtraInfo()); - } else if (type.equals("container")) { - return ItemContainer.CODEC.decode(parse, new ExtraInfo()); - } - return null; - } - }) - .register(); - registration.newType(ItemStack.class, "itemstack", "itemStack@s") - .name("Item Stack") - .description("Represents an item in an inventory slot.") - .examples("set {_i} to itemstack of Food_Fish_Grilled") - .since("1.0.0") - .toStringFunction(itemStack -> { - String quantity = itemStack.getQuantity() == 1 ? "" : itemStack.getQuantity() + " of "; - return "itemstack of " + quantity + itemStack.getItem().getId(); - }) - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull ItemStack value) { - BsonDocument encode = ItemStack.CODEC.encode(value, new ExtraInfo()); - String json = encode.toJson(); - return gson.fromJson(json, JsonElement.class); - } - - @Override - public ItemStack deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - return ItemStack.CODEC.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); - } - }) - .register(); - registration.newType(Inventory.class, "inventory", "inventor@y@ies") - .name("Inventory") - .description("Represents an inventory of an entity or block.") - .since("1.0.0") - .toStringFunction(Inventory::toString) - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull Inventory value) { - BsonDocument encode = Inventory.CODEC.encode(value, new ExtraInfo()); - return gson.fromJson(encode.toJson(), JsonElement.class); - } - - @Override - public Inventory deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - return Inventory.CODEC.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); - } - }) - .register(); - EnumRegistry.register(registration, InventoryActionType.class, "inventoryactiontype", "inventoryActionType@s") - .name("Inventory Action Type") - .description("Represents the types of actions that can be performed in an inventory.") - .since("1.0.0") - .register(); - } - - private static void registerBlockTypes(SkriptRegistration registration) { - registration.newType(Block.class, "block", "block@s") - .name("Block") - .description("Represents a block in a world.") - .since("1.0.0") - .toStringFunction(Block::toTypeString) - .register(); - } - - private static void registerWorldTypes(SkriptRegistration registration) { - registration.newType(World.class, "world", "world@s") - .name("World") - .description("Represents a world in the game.") - .since("1.0.0") - .toStringFunction(World::getName) - .register(); - registration.newType(WorldChunk.class, "chunk", "chunk@s") - .name("Chunk") - .description("Represents a chunk in a world. A chunk is a 32x32x(world height) set of blocks.") - .since("1.0.0") - .toStringFunction(worldChunk -> "chunk (x=" + worldChunk.getX() + ",z=" + worldChunk.getZ() + ") in world '" + worldChunk.getWorld().getName() + "'") - .register(); - } - - private static void registerAssetStoreTypes(SkriptRegistration registration) { - AssetStoreRegistry.register(registration, BiomeAsset.class, BiomeAsset.getAssetStore().getAssetMap(), "biome", "biome@s") - .name("Biome") - .description("Represents the types of biomes in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(BiomeAsset::getId) - .register(); - AssetStoreRegistry.register(registration, BlockGroup.class, AssetRegistry.getAssetStore(BlockGroup.class).getAssetMap(), - "blockgroup", "blockgroup@s") - .name("Block Group") - .description("Represents the groups of blocks in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(BlockGroup::getId) - .register(); - AssetStoreRegistry.register(registration, CraftingRecipe.class, CraftingRecipe.getAssetMap(), "craftingrecipe", "craftingrecipe@s") - .name("Crafting Recipe") - .description("Represents the crafting recipes in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(CraftingRecipe::getId) - .register(); - AssetStoreRegistry.register(registration, BlockType.class, BlockType.getAssetMap(), "blocktype", "blockType@s") - .name("BlockType") - .description("Represents the types of blocks in the game.", autoGenMessage()) - .examples("set {_block} to blocktype of block at player") - .since("1.0.0") - .toStringFunction(BlockType::getId) - .register(); - AssetStoreRegistry.register(registration, Fluid.class, Fluid.getAssetMap(), "fluid", "fluid@s") - .name("Fluid") - .description("Represents the types of fluids in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(Fluid::getId) - .register(); - AssetStoreRegistry.register(registration, EntityEffect.class, EntityEffect.getAssetMap(), - "entityeffect", "entityEffect@s") - .name("Entity Effect") - .description("Represents the types of effects that can be applied to entities.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(EntityEffect::getId) - .register(); - AssetStoreRegistry.register(registration, EntityStatType.class, EntityStatType.getAssetMap(), - "entitystattype", "entityStatType@s") - .name("Entity Stat Type") - .description("Represents the types of stats that can be applied to entities.", autoGenMessage()) - .since("1.0.0") - .register(); - AssetStoreRegistry.register(registration, Environment.class, Environment.getAssetMap(), - "environment", "environment@s") - .name("Environment") - .description("Represents the types of environments in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(Environment::getId) - .register(); - AssetStoreRegistry.register(registration, DamageCause.class, DamageCause.getAssetMap(), - "damagecause", "damageCause@s") - .name("Damage Cause") - .description("Represents the types of damage that can be caused to entities.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(DamageCause::getId) - .register(); - AssetStoreRegistry.register(registration, Interaction.class, Interaction.getAssetMap(), - "interaction", "interaction@s") - .name("Interaction") - .description("Represents the types of interactions that can be performed by entities.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(Interaction::getId) - .register(); - AssetStoreRegistry.register(registration, Item.class, Item.getAssetMap(), "item", "item@s") - .name("Item") - .description("Represents the types of items in the game.", autoGenMessage()) - .examples("set {_i} to itemstack of Food_Fish_Grilled") - .since("1.0.0") - .toStringFunction(Item::getId) - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull Item value) { - BsonValue encode = Item.CODEC.encode(value, new ExtraInfo()); - return gson.fromJson(encode.asDocument().toJson(), JsonElement.class); - } - - @Override - public Item deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - return Item.CODEC.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); - } - }) - .register(); - AssetStoreRegistry.register(registration, Projectile.class, Projectile.getAssetMap(), "projectile", "projectile@s") - .name("Projectile") - .description("Represents the types of projectiles in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(Projectile::getId) - .register(); - AssetStoreRegistry.register(registration, ResourceType.class, ResourceType.getAssetMap(), "resourcetype", "resourceType@s") - .name("Resource Type") - .description("Represents the types of resources in the game, such as woods and stones.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(ResourceType::getId) - .register(); - AssetStoreRegistry.register(registration, SoundEvent.class, SoundEvent.getAssetMap(), "soundevent", "soundevent@s") - .name("Sound Event") - .description("Represents the types of sounds in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(SoundEvent::getId) - .register(); - AssetStoreRegistry.register(registration, Weather.class, Weather.getAssetMap(), "weather", "weather@s") - .name("Weather") - .description("Represents the types of weather in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(Weather::getId) - .register(); - } - - private static String autoGenMessage() { - String serverVersion = ManifestUtil.getImplementationVersion(); - return "This type is auto-generated with values from Hytale." + - "\nCurrently generated with Hytale Server version `" + serverVersion + "`."; - - } - } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesAssetStore.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesAssetStore.java new file mode 100644 index 00000000..d70b41fa --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesAssetStore.java @@ -0,0 +1,151 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.skript.registration.AssetStoreRegistry; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.hypixel.hytale.assetstore.AssetRegistry; +import com.hypixel.hytale.builtin.hytalegenerator.assets.biomes.BiomeAsset; +import com.hypixel.hytale.codec.ExtraInfo; +import com.hypixel.hytale.common.util.java.ManifestUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; +import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; +import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; +import com.hypixel.hytale.server.core.asset.type.item.config.BlockGroup; +import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.asset.type.item.config.ResourceType; +import com.hypixel.hytale.server.core.asset.type.projectile.config.Projectile; +import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; +import com.hypixel.hytale.server.core.asset.type.weather.config.Weather; +import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; +import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; +import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; +import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; +import org.bson.BsonDocument; +import org.bson.BsonValue; +import org.jetbrains.annotations.NotNull; + +public class TypesAssetStore { + + static void register(SkriptRegistration registration) { + AssetStoreRegistry.register(registration, BiomeAsset.class, BiomeAsset.getAssetStore().getAssetMap(), "biome", "biome@s") + .name("Biome") + .description("Represents the types of biomes in the game.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(BiomeAsset::getId) + .register(); + AssetStoreRegistry.register(registration, BlockGroup.class, AssetRegistry.getAssetStore(BlockGroup.class).getAssetMap(), + "blockgroup", "blockgroup@s") + .name("Block Group") + .description("Represents the groups of blocks in the game.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(BlockGroup::getId) + .register(); + AssetStoreRegistry.register(registration, CraftingRecipe.class, CraftingRecipe.getAssetMap(), "craftingrecipe", "craftingrecipe@s") + .name("Crafting Recipe") + .description("Represents the crafting recipes in the game.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(CraftingRecipe::getId) + .register(); + AssetStoreRegistry.register(registration, BlockType.class, BlockType.getAssetMap(), "blocktype", "blockType@s") + .name("BlockType") + .description("Represents the types of blocks in the game.", autoGenMessage()) + .examples("set {_block} to blocktype of block at player") + .since("1.0.0") + .toStringFunction(BlockType::getId) + .register(); + AssetStoreRegistry.register(registration, Fluid.class, Fluid.getAssetMap(), "fluid", "fluid@s") + .name("Fluid") + .description("Represents the types of fluids in the game.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(Fluid::getId) + .register(); + AssetStoreRegistry.register(registration, EntityEffect.class, EntityEffect.getAssetMap(), + "entityeffect", "entityEffect@s") + .name("Entity Effect") + .description("Represents the types of effects that can be applied to entities.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(EntityEffect::getId) + .register(); + AssetStoreRegistry.register(registration, EntityStatType.class, EntityStatType.getAssetMap(), + "entitystattype", "entityStatType@s") + .name("Entity Stat Type") + .description("Represents the types of stats that can be applied to entities.", autoGenMessage()) + .since("1.0.0") + .register(); + AssetStoreRegistry.register(registration, Environment.class, Environment.getAssetMap(), + "environment", "environment@s") + .name("Environment") + .description("Represents the types of environments in the game.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(Environment::getId) + .register(); + AssetStoreRegistry.register(registration, DamageCause.class, DamageCause.getAssetMap(), + "damagecause", "damageCause@s") + .name("Damage Cause") + .description("Represents the types of damage that can be caused to entities.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(DamageCause::getId) + .register(); + AssetStoreRegistry.register(registration, Interaction.class, Interaction.getAssetMap(), + "interaction", "interaction@s") + .name("Interaction") + .description("Represents the types of interactions that can be performed by entities.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(Interaction::getId) + .register(); + AssetStoreRegistry.register(registration, Item.class, Item.getAssetMap(), "item", "item@s") + .name("Item") + .description("Represents the types of items in the game.", autoGenMessage()) + .examples("set {_i} to itemstack of Food_Fish_Grilled") + .since("1.0.0") + .toStringFunction(Item::getId) + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull Item value) { + BsonValue encode = Item.CODEC.encode(value, new ExtraInfo()); + return gson.fromJson(encode.asDocument().toJson(), JsonElement.class); + } + + @Override + public Item deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + return Item.CODEC.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); + } + }) + .register(); + AssetStoreRegistry.register(registration, Projectile.class, Projectile.getAssetMap(), "projectile", "projectile@s") + .name("Projectile") + .description("Represents the types of projectiles in the game.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(Projectile::getId) + .register(); + AssetStoreRegistry.register(registration, ResourceType.class, ResourceType.getAssetMap(), "resourcetype", "resourceType@s") + .name("Resource Type") + .description("Represents the types of resources in the game, such as woods and stones.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(ResourceType::getId) + .register(); + AssetStoreRegistry.register(registration, SoundEvent.class, SoundEvent.getAssetMap(), "soundevent", "soundevent@s") + .name("Sound Event") + .description("Represents the types of sounds in the game.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(SoundEvent::getId) + .register(); + AssetStoreRegistry.register(registration, Weather.class, Weather.getAssetMap(), "weather", "weather@s") + .name("Weather") + .description("Represents the types of weather in the game.", autoGenMessage()) + .since("1.0.0") + .toStringFunction(Weather::getId) + .register(); + } + + private static String autoGenMessage() { + String serverVersion = ManifestUtil.getImplementationVersion(); + String patchline = ManifestUtil.getPatchline(); + return "This type is auto-generated with values from Hytale." + + "\nCurrently generated with Hytale Server version `" + serverVersion + " (" + patchline + ")`."; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesBlock.java new file mode 100644 index 00000000..f1927500 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesBlock.java @@ -0,0 +1,17 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; + +public class TypesBlock { + + static void register(SkriptRegistration registration) { + registration.newType(Block.class, "block", "block@s") + .name("Block") + .description("Represents a block in a world.") + .since("1.0.0") + .toStringFunction(Block::toTypeString) + .register(); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesCustom.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesCustom.java new file mode 100644 index 00000000..63a43a56 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesCustom.java @@ -0,0 +1,19 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.hytale.Direction; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; + +public class TypesCustom { + + static void register(SkriptRegistration reg) { + reg.newType(Direction.class, "direction", "direction@s") + .name("Direction") + .description("Represents a direction in the world.") + .usage(Direction.getUsageString()) + .literalParser(Direction::parse) + .toStringFunction(Direction::getName) + .since("1.0.0") + .register(); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesEntity.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesEntity.java new file mode 100644 index 00000000..96d89dc6 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesEntity.java @@ -0,0 +1,55 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.skript.registration.NPCRegistry; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.npc.entities.NPCEntity; + +public class TypesEntity { + + static void register(SkriptRegistration registration) { + registration.newType(NPCRegistry.NPCRole.class, "npcrole", "npcrole@s") + .name("NPC Role") + .description("Represents the type of NPCs in the game.") + .examples("coming soon") // TODO + .usage(NPCRegistry.getTypeUsage()) + .since("1.0.0") + .toStringFunction(NPCRegistry.NPCRole::name) + .literalParser(NPCRegistry::parse) + .register(); + registration.newType(Entity.class, "entity", "entit@y@ies") + .toStringFunction(Entity::toString) // TODO get its name or something + .name("Entity") + .description("Represents any entity in the game, including players and mobs.") + .since("1.0.0") + .register(); + registration.newType(LivingEntity.class, "livingentity", "livingEntit@y@ies") + .name("Living Entity") + .description("Represents any living entity in the game, including players and mobs.") + .since("1.0.0") + .toStringFunction(LivingEntity::getLegacyDisplayName) + .register(); + registration.newType(NPCEntity.class, "npcentity", "npcEntit@y@ies") + .name("NPC Entity") + .description("Represents an NPC entity in the game.") + .since("1.0.0") + .toStringFunction(NPCRegistry::stringify) + .register(); + registration.newType(Player.class, "player", "player@s") + .name("Player") + .description("Represents a player in the game.") + .since("1.0.0") + .toStringFunction(Player::getDisplayName) + .register(); + registration.newType(PlayerRef.class, "playerref", "playerRef@s") + .name("Player Ref") + .description("Represents a reference to a player in the game.") + .since("1.0.0") + .toStringFunction(PlayerRef::getUsername) + .register(); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesItem.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesItem.java new file mode 100644 index 00000000..8cf25344 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesItem.java @@ -0,0 +1,115 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.skript.registration.EnumRegistry; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.variables.SerializerUtils; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.hypixel.hytale.codec.ExtraInfo; +import com.hypixel.hytale.protocol.InventoryActionType; +import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; +import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; +import org.bson.BsonDocument; +import org.bson.BsonString; +import org.jetbrains.annotations.NotNull; + +public class TypesItem { + + static void register(SkriptRegistration registration) { + // Please keep in alphabetical order + registration.newType(ItemComponent.class, "itemcomponent", "itemComponent@s") + .name("Item Component") + .description("Represents the component of a dropped item.") + .since("INSERT VERSION") + .toStringFunction(ic -> String.format("ItemComponent{itemstack=%s}", ic.getItemStack())) + .serializer(SerializerUtils.getCodecSerializer(ItemComponent.CODEC)) + .register(); + registration.newType(ItemContainer.class, "itemcontainer", "itemContainer@s") + .name("Item Container") + .description("Represents an item container within an inventory (such as the armor container).") + .since("1.0.0") + .experimental() + .toStringFunction(ItemContainer::toString) + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull ItemContainer value) { + BsonDocument document; + if (value instanceof CombinedItemContainer cic) { + document = CombinedItemContainer.CODEC.encode(cic, new ExtraInfo()).asDocument(); + document.put("type", new BsonString("combined")); + } else { + document = ItemContainer.CODEC.encode(value, new ExtraInfo()).asDocument(); + document.put("type", new BsonString("container")); + } + + return gson.fromJson(document.toJson(), JsonElement.class); + } + + @Override + public ItemContainer deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + BsonDocument parse = BsonDocument.parse(element.toString()); + String type = parse.getString("type").getValue(); + if (type == null) return null; + + if (type.equals("combined")) { + return CombinedItemContainer.CODEC.decode(parse, new ExtraInfo()); + } else if (type.equals("container")) { + return ItemContainer.CODEC.decode(parse, new ExtraInfo()); + } + return null; + } + }) + .register(); + registration.newType(ItemStack.class, "itemstack", "itemStack@s") + .name("Item Stack") + .description("Represents an item in an inventory slot.") + .examples("set {_i} to itemstack of Food_Fish_Grilled") + .since("1.0.0") + .toStringFunction(itemStack -> { + String quantity = itemStack.getQuantity() == 1 ? "" : itemStack.getQuantity() + " of "; + return "itemstack of " + quantity + itemStack.getItem().getId(); + }) + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull ItemStack value) { + BsonDocument encode = ItemStack.CODEC.encode(value, new ExtraInfo()); + String json = encode.toJson(); + return gson.fromJson(json, JsonElement.class); + } + + @Override + public ItemStack deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + return ItemStack.CODEC.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); + } + }) + .register(); + registration.newType(Inventory.class, "inventory", "inventor@y@ies") + .name("Inventory") + .description("Represents an inventory of an entity or block.") + .since("1.0.0") + .toStringFunction(Inventory::toString) + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull Inventory value) { + BsonDocument encode = Inventory.CODEC.encode(value, new ExtraInfo()); + return gson.fromJson(encode.toJson(), JsonElement.class); + } + + @Override + public Inventory deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + return Inventory.CODEC.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); + } + }) + .register(); + EnumRegistry.register(registration, InventoryActionType.class, "inventoryactiontype", "inventoryActionType@s") + .name("Inventory Action Type") + .description("Represents the types of actions that can be performed in an inventory.") + .since("1.0.0") + .register(); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesJava.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesJava.java new file mode 100644 index 00000000..b34b83fe --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesJava.java @@ -0,0 +1,34 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class TypesJava { + + static void register(SkriptRegistration registration) { + registration.newType(UUID.class, "uuid", "uuid@s") + .name("UUID") + .description("Represents a UUID.") + .examples("set {_uuid} to uuid of {_player}") + .since("1.0.0") + .toStringFunction(UUID::toString) + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull UUID value) { + return gson.toJsonTree(value.toString()); + } + + @Override + public UUID deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + return UUID.fromString(element.getAsString()); + } + }) + .register(); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesServer.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesServer.java new file mode 100644 index 00000000..858548bf --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesServer.java @@ -0,0 +1,173 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.skript.command.ArgUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.hypixel.hytale.codec.ExtraInfo; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandSender; +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType; +import com.hypixel.hytale.server.core.modules.entity.damage.Damage; +import com.hypixel.hytale.server.core.receiver.IMessageReceiver; +import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; +import org.bson.BsonDocument; +import org.bson.BsonString; +import org.bson.BsonValue; +import org.jetbrains.annotations.NotNull; + +public class TypesServer { + + static void register(SkriptRegistration registration) { + registration.newType(ArgumentType.class, "argumenttype", "argumentType@s") + .name("Argument Type") + .description("Represents the types of arguments that can be used in commands.") + .usage(ArgUtils.getTypeUsage()) + .since("1.0.0") + .register(); + registration.newType(CommandSender.class, "commandsender", "commandSender@s") + .name("Command Sender") + .description("Represents a command sender such as a player or the console.") + .since("1.0.0") + .toStringFunction(CommandSender::getDisplayName) + .register(); + registration.newType(Damage.class, "damage", "damage@s") + .name("Damage") + .description("Represents information about damage dealt to an entity.") + .since("1.0.0") + .register(); + registration.newType(Damage.Source.class, "damagesource", "damageSource@s") + .name("Damage Source") + .description("Represents the source of damage when an entity is damaged/killed.") + .since("1.0.0") + .register(); + registration.newType(IMessageReceiver.class, "messagereceiver", "messageReceiver@s") + .name("Message Receiver") + .description("Represents a receiver of messages such as a player or the console.") + .since("1.0.0") + .register(); + registration.newType(Message.class, "message", "message@s") + .name("Message") + .description("Represents a stylized message sent to a message receiver.") + .since("1.0.0") + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull Message value) { + BsonValue encode = Message.CODEC.encode(value, new ExtraInfo()); + return gson.fromJson(encode.asDocument().toJson(), JsonElement.class); + } + + @Override + public Message deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + BsonDocument decode = BsonDocument.parse(element.toString()); + return Message.CODEC.decode(decode, new ExtraInfo()); + } + }) + .register(); + registration.newType(HytaleServer.class, "server", "server@s") + .name("Server") + .description("Represents the Hytale server.") + .since("1.0.0") + .register(); + registration.newType(Vector3f.class, "vector3f", "vector3f@s") + .name("Vector3f") + .description("Represents a vector in 3D space using floats.", + "Often used for the rotation of entities in a world.") + .since("1.0.0") + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull Vector3f value) { + BsonDocument encode = Vector3f.CODEC.encode(value, new ExtraInfo()); + return gson.fromJson(encode.toJson(), JsonElement.class); + } + + @Override + public Vector3f deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + BsonDocument decode = BsonDocument.parse(element.toString()); + return Vector3f.CODEC.decode(decode, new ExtraInfo()); + } + }) + .register(); + registration.newType(Vector3d.class, "vector3d", "vector3d@s") + .name("Vector3d") + .description("Represents a vector in 3D space using doubles.", + "Often used for the position of entities in a world.") + .since("1.0.0") + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull Vector3d value) { + BsonDocument encode = Vector3d.CODEC.encode(value, new ExtraInfo()); + return gson.fromJson(encode.toJson(), JsonElement.class); + } + + @Override + public Vector3d deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + BsonDocument decode = BsonDocument.parse(element.toString()); + return Vector3d.CODEC.decode(decode, new ExtraInfo()); + } + }) + .register(); + registration.newType(Vector3i.class, "vector3i", "vector3i@s") + .name("Vector3i") + .description("Represents a vector in 3D space using integers.", + "Often used for the position of blocks in a world.") + .since("1.0.0") + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull Vector3i value) { + BsonDocument encode = Vector3i.CODEC.encode(value, new ExtraInfo()); + return gson.fromJson(encode.toJson(), JsonElement.class); + } + + @Override + public Vector3i deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + BsonDocument decode = BsonDocument.parse(element.toString()); + return Vector3i.CODEC.decode(decode, new ExtraInfo()); + } + }) + .register(); + registration.newType(Location.class, "location", "location@s") + .name("Location") + .description("Represents a location in a world.", + "A location contains a world, a position (vector3d) and a rotation (vector3f).") + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull Location location) { + BsonDocument bsonDocument = new BsonDocument(); + String world = location.getWorld(); + if (world != null) { + // This shouldn't be null, but let's be safe + bsonDocument.put("world", new BsonString(world)); + } + bsonDocument.put("position", Vector3d.CODEC.encode(location.getPosition(), new ExtraInfo())); + bsonDocument.put("rotation", Vector3f.CODEC.encode(location.getRotation(), new ExtraInfo())); + + return gson.fromJson(bsonDocument.toJson(), JsonElement.class); + } + + @Override + public Location deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + BsonDocument decode = BsonDocument.parse(element.toString()); + Vector3d position = Vector3d.CODEC.decode(decode.get("position"), new ExtraInfo()); + Vector3f rotation = Vector3f.CODEC.decode(decode.get("rotation"), new ExtraInfo()); + + String world = null; + if (decode.containsKey("world")) { + // This shouldn't be null, but let's be safe + world = decode.getString("world").getValue(); + } + assert position != null; + assert rotation != null; + return new Location(world, position, rotation); + } + }) + .since("1.0.0") + .register(); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesWorld.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesWorld.java new file mode 100644 index 00000000..fc8bba3d --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesWorld.java @@ -0,0 +1,24 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; + +public class TypesWorld { + + static void register(SkriptRegistration registration) { + registration.newType(World.class, "world", "world@s") + .name("World") + .description("Represents a world in the game.") + .since("1.0.0") + .toStringFunction(World::getName) + .register(); + registration.newType(WorldChunk.class, "chunk", "chunk@s") + .name("Chunk") + .description("Represents a chunk in a world. A chunk is a 32x32x(world height) set of blocks.") + .since("1.0.0") + .toStringFunction(worldChunk -> "chunk (x=" + worldChunk.getX() + ",z=" + worldChunk.getZ() + ") in world '" + worldChunk.getWorld().getName() + "'") + .register(); + } + +} diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk index 97a48909..2d0f390e 100644 --- a/src/main/resources/config.sk +++ b/src/main/resources/config.sk @@ -3,6 +3,22 @@ debug: false # Whether or not to enable debug mode (maybe print more verbose logs to console). +# Effect commands allow you to execute effects in chat. +effect-commands: + enabled: true + # Whether or not effect commands are enabled. + + allow-ops: false + # Whether or not ops can use effect commands. + # If false, they will require the permission specified below in 'required-permission'. + # This currently does not work as Hytale does not support permissions this way. + + required-permission: hyskript.effect-commands + # The permission required to use effect commands (if allow-ops is false). + + token: ! + # The token used to execute effects in chat. + # Represents the database[s] used by HySkript to save variables to. # You can add as many databases as you want. databases: