1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-24 10:27:38 +00:00

Clean up Javadocs a little

I've no motivation for modding right now, but always got time for build
system busywork!

CC:T (and CC before that) has always published its API docs. However,
they're not always the most helpful — they're useful if you know what
you're looking for, but aren't a good getting-started guide.

Part of the issue here is there's no examples, and everything is
described pretty abstractly. I have occasionally tried to improve this
(e.g. the peripheral docs in bdffabc08e),
but it's a long road.

This commit adds a new example mod, which registers peripherals, an API
and a turtle upgrade. While the mod itself isn't exported as part of the
docs, we reference blocks of it using Java's new {@snippet} tag.

 - Switch the Forge project to use NeoForge's new Legacy MDG plugin. We
   don't *need* to do this, but it means the build logic for Forge and
   NeoForge is more closely aligned.

 - Add a new SnippetTaglet, which is a partial backport of Java 18+'s
   {@snippet}.

 - Add an example mod. This is a working multi-loader mod, complete with
   datagen (albeit with no good multi-loader abstractions).

 - Move our existing <pre>{@code ...}</pre> blocks into the example mod,
   replacing them with {@snippet}s.

 - Add a new overview page to the docs, providing some getting-started
   information. We had this already in the dan200.computercraft.api
   package docs, but it's not especially visible there.
This commit is contained in:
Jonathan Coates
2025-01-09 20:47:51 +00:00
parent d9fc1c3a80
commit 3c46b8acd7
57 changed files with 1089 additions and 616 deletions

View File

@@ -11,12 +11,6 @@ plugins {
id("cc-tweaked.publishing")
}
sourceSets {
main {
resources.srcDir("src/generated/resources")
}
}
minecraft {
accessWideners(
"src/main/resources/computercraft.accesswidener",
@@ -113,20 +107,28 @@ val lintLua by tasks.registering(IlluaminateExec::class) {
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
}
val runData by tasks.registering(MergeTrees::class) {
output = layout.projectDirectory.dir("src/generated/resources")
fun MergeTrees.configureForDatagen(source: SourceSet, outputFolder: String) {
output = layout.projectDirectory.dir(outputFolder)
for (loader in listOf("forge", "fabric")) {
mustRunAfter(":$loader:runData")
mustRunAfter(":$loader:$name")
source {
input {
from(project(":$loader").layout.buildDirectory.dir("generatedResources"))
from(project(":$loader").layout.buildDirectory.dir(source.getTaskName("generateResources", null)))
exclude(".cache")
}
output = project(":$loader").layout.projectDirectory.dir("src/generated/resources")
output = project(":$loader").layout.projectDirectory.dir(outputFolder)
}
}
}
val runData by tasks.registering(MergeTrees::class) {
configureForDatagen(sourceSets.main.get(), "src/generated/resources")
}
val runExampleData by tasks.registering(MergeTrees::class) {
configureForDatagen(sourceSets.examples.get(), "src/examples/generatedResources")
}
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }

View File

@@ -0,0 +1,4 @@
{
"type": "examplemod:example_turtle_upgrade",
"item": "minecraft:compass"
}

View File

@@ -0,0 +1,75 @@
package com.example.examplemod;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.component.ComputerComponents;
import dan200.computercraft.api.lua.Coerced;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.turtle.ITurtleAccess;
import org.jetbrains.annotations.Nullable;
/**
* An example API that will be available on every turtle. This demonstrates both registering an API, and how to write
* Lua-facing functions.
* <p>
* This API is not available as a global (as {@link #getNames() returns nothing}), but is instead accessible via
* {@code require} (see {@link #getModuleName()}).
*
* <h2>Example</h2>
* <pre class="language language-lua">{@code
* local my_api = require("example.my_api")
* print("Turtle is facing " .. my_api.getDirection())
* }</pre>
*/
public class ExampleAPI implements ILuaAPI {
private final ITurtleAccess turtle;
public ExampleAPI(ITurtleAccess turtle) {
this.turtle = turtle;
}
public static void register() {
// @start region=register
ComputerCraftAPI.registerAPIFactory(computer -> {
// Read the turtle component.
var turtle = computer.getComponent(ComputerComponents.TURTLE);
// If present then add our API.
return turtle == null ? null : new ExampleAPI(turtle);
});
// @end region=register
}
@Override
public String[] getNames() {
return new String[0];
}
@Override
public @Nullable String getModuleName() {
return "example.my_api";
}
/**
* A Lua-facing function function that returns the direction the turtle is facing.
*
* @return The turtle's direction.
*/
@LuaFunction
public final String getDirection() {
return turtle.getDirection().getName();
}
/**
* A Lua-facing function using {@link Coerced}. Unlike a {@link LuaFunction} taking a raw {@link String}, this will
* accept any value, and convert it to a string.
*
* @param myString The value to write.
*/
// @start region=coerced
@LuaFunction
public final void writeString(Coerced<String> myString) {
String contents = myString.value();
System.out.println("Got " + contents);
}
// @end region=coerced
}

View File

@@ -0,0 +1,39 @@
package com.example.examplemod;
import com.example.examplemod.data.TurtleDataProvider;
import com.example.examplemod.peripheral.FurnacePeripheral;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
/**
* Our example mod, containing the various things we register.
* <p>
* This isn't an especially good template to follow! It's convenient for our example mod (as we need to be multi-loader
* compatible), but there's a good chance there's a better pattern to follow. For example, on Forge you'd use
* {@code DeferredRegister} to register things), and multi-loader mods probably have their own abstractions.
* <p>
* See {@code FabricExampleMod} and {@code ForgeExampleMod} for the actual mod entrypoints.
*/
public final class ExampleMod {
public static final String MOD_ID = "examplemod";
/**
* The upgrade serialiser for our example turtle upgrade. See the documentation for {@link TurtleUpgradeSerialiser}
* or {@code FabricExampleMod}/{@code ForgeExampleMod} for how this is registered.
* <p>
* This only defines the upgrade type. See {@link TurtleDataProvider} for defining the actual upgrade.
*/
// @start region=turtle_upgrades
public static final TurtleUpgradeSerialiser<ExampleTurtleUpgrade> EXAMPLE_TURTLE_UPGRADE = TurtleUpgradeSerialiser.simpleWithCustomItem(
ExampleTurtleUpgrade::new
);
// @end region=turtle_upgrades
public static void registerComputerCraft() {
// @start region=generic_source
ComputerCraftAPI.registerGenericSource(new FurnacePeripheral());
// @end region=generic_source
ExampleAPI.register();
}
}

View File

@@ -0,0 +1,17 @@
package com.example.examplemod;
import dan200.computercraft.api.turtle.AbstractTurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
/**
* An example turtle upgrade.
*/
// @start region=body
public class ExampleTurtleUpgrade extends AbstractTurtleUpgrade {
public ExampleTurtleUpgrade(ResourceLocation id, ItemStack stack) {
super(id, TurtleUpgradeType.PERIPHERAL, stack);
}
}
// @end region=body

View File

@@ -0,0 +1,17 @@
package com.example.examplemod.data;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
/**
* The entry point to example mod's data-generators.
* <p>
* This is called by our platform-specific entry-point (see {@code FabricExampleModDataGenerator} and
* {@code ForgeExampleModDataGenerator}. That said, the exact setup isn't relevant (it will vary depending on
* mod-loader), what's interesting is the contents of the {@link #run(DataGenerator.PackGenerator)} method!
*/
public final class ExampleModDataGenerators {
public static void run(DataGenerator.PackGenerator pack) {
pack.addProvider((DataProvider.Factory<?>) TurtleDataProvider::new);
}
}

View File

@@ -0,0 +1,34 @@
package com.example.examplemod.data;
import com.example.examplemod.ExampleMod;
import com.example.examplemod.ExampleTurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Items;
import java.util.function.Consumer;
/**
* A {@link TurtleUpgradeDataProvider} that generates the JSON for our {@linkplain ExampleTurtleUpgrade example
* upgrade}.
*
* @see ExampleModDataGenerators
*/
// @start region=body
public class TurtleDataProvider extends TurtleUpgradeDataProvider {
public TurtleDataProvider(PackOutput output) {
super(output);
}
@Override
protected void addUpgrades(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade) {
simpleWithCustomItem(
new ResourceLocation(ExampleMod.MOD_ID, "example_turtle_upgrade"),
ExampleMod.EXAMPLE_TURTLE_UPGRADE,
Items.COMPASS
).add(addUpgrade);
}
}
// @end region=body

View File

@@ -0,0 +1,12 @@
@ApiStatus.Internal
@DefaultQualifier(value = NonNull.class, locations = {
TypeUseLocation.RETURN,
TypeUseLocation.PARAMETER,
TypeUseLocation.FIELD,
})
package com.example.examplemod;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.checkerframework.framework.qual.TypeUseLocation;
import org.jetbrains.annotations.ApiStatus;

View File

@@ -0,0 +1,39 @@
package com.example.examplemod.peripheral;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
import org.jetbrains.annotations.Nullable;
/**
* A peripheral that adds a {@code getFuel()} method to brewing stands. This demonstrates the usage of
* {@link IPeripheral}.
*
* @see dan200.computercraft.api.peripheral
* @see FurnacePeripheral Using {@code GenericPeripheral}.
*/
// @start region=body
public class BrewingStandPeripheral implements IPeripheral {
private final BrewingStandBlockEntity brewingStand;
public BrewingStandPeripheral(BrewingStandBlockEntity brewingStand) {
this.brewingStand = brewingStand;
}
@Override
public String getType() {
return "brewing_stand";
}
@LuaFunction
public final int getFuel() {
// Don't do it this way! Use an access widener/transformer to access the "fuel" field instead.
return brewingStand.saveWithoutMetadata().getInt("Fuel");
}
@Override
public boolean equals(@Nullable IPeripheral other) {
return other instanceof BrewingStandPeripheral o && brewingStand == o.brewingStand;
}
}
// @end region=body

View File

@@ -0,0 +1,44 @@
package com.example.examplemod.peripheral;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.AttachedComputerSet;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import org.jetbrains.annotations.Nullable;
/**
* A peripheral that tracks what computers it is attached to.
*
* @see AttachedComputerSet
*/
// @start region=body
public class ComputerTrackingPeripheral implements IPeripheral {
private final AttachedComputerSet computers = new AttachedComputerSet();
@Override
public void attach(IComputerAccess computer) {
computers.add(computer);
}
@Override
public void detach(IComputerAccess computer) {
computers.remove(computer);
}
@LuaFunction
public final void sayHello() {
// Queue a "hello" event on each computer.
computers.forEach(x -> x.queueEvent("hello", x.getAttachmentName()));
}
@Override
public String getType() {
return "my_peripheral";
}
@Override
public boolean equals(@Nullable IPeripheral other) {
return this == other;
}
}
// @end region=body

View File

@@ -0,0 +1,29 @@
package com.example.examplemod.peripheral;
import com.example.examplemod.ExampleMod;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
/**
* A peripheral that adds a {@code getBurnTime} method to furnaces. This is used to demonstrate the usage of
* {@link GenericPeripheral}.
*
* @see dan200.computercraft.api.peripheral
* @see BrewingStandPeripheral Using {@code IPeripheral}.
*/
// @start region=body
public class FurnacePeripheral implements GenericPeripheral {
@Override
public String id() {
return new ResourceLocation(ExampleMod.MOD_ID, "furnace").toString();
}
@LuaFunction(mainThread = true)
public int getBurnTime(AbstractFurnaceBlockEntity furnace) {
// Don't do it this way! Use an access widener/transformer to access the "litTime" field instead.
return furnace.saveWithoutMetadata().getInt("BurnTime");
}
}
// @end region=body