Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
34 changes: 32 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ plugins {

project.version = "0.0.1-SNAPSHOT"

import net.fabricmc.loom.task.RunClientTask

allprojects {
project.group = "textile-api"

Expand All @@ -31,12 +33,30 @@ allprojects {
}
}

sourceSets {
testmod {
compileClasspath += main.compileClasspath
runtimeClasspath += main.runtimeClasspath
}
}

task runTestmodClient(type: RunClientTask) {
classpath sourceSets.testmod.runtimeClasspath
}

dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"

mappings "net.textilemc:yarrn:inf-20100618+build20d332:v2"

modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"

// IMPORTANT
// Until a certain pr is merged, make sure you
// add -Dfabric.gameVersion=20100618 to your run
// config's vm arguments otherwise bad

modImplementation "com.github.sfPlayer1:fabric-loader:adad4b3"

// Some dependencies aren't gotten by the loader, so we add them ourselves
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.8.1'
Expand Down Expand Up @@ -104,6 +124,16 @@ allprojects {
}
}

subprojects {
dependencies {
testmodCompile sourceSets.main.output
}
}

sourceSets {
testmod
}

javadoc {
options {
source = "8"
Expand All @@ -114,7 +144,7 @@ javadoc {
'https://guava.dev/releases/21.0/api/docs/',
'https://asm.ow2.io/javadoc/',
'https://docs.oracle.com/javase/8/docs/api/',
'http://jenkins.liteloader.com/job/Mixin/javadoc/',
'https://jenkins.liteloader.com/job/Mixin/javadoc/',
'https://logging.apache.org/log4j/2.x/log4j-api/apidocs/'
// Need to add minecraft jd publication etc once there is one available
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.textilemc.textile.api.util;

import java.util.Objects;

import org.jetbrains.annotations.NotNull;

import net.minecraft.client.MinecraftApplet;
import net.minecraft.client.MinecraftClient;

/**
* Utilities to get game instances.
*/
@SuppressWarnings("FieldMayBeFinal")
public class GameInstanceUtils {
private static MinecraftClient CLIENT = null;
private static MinecraftApplet APPLET = null;

@NotNull
public static MinecraftClient getMinecraftClient() {
return Objects.requireNonNull(CLIENT, "Requested Minecraft client too early!");
}

@NotNull
public static MinecraftApplet getMinecraftApplet() {
return Objects.requireNonNull(APPLET, "Requested Minecraft applet too early!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package net.textilemc.textile.mixin.base;

import java.lang.reflect.Field;

import net.textilemc.textile.api.util.GameInstanceUtils;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import net.minecraft.client.MinecraftApplet;
import net.minecraft.client.MinecraftClient;

@Mixin(MinecraftApplet.class)
public class MinecraftAppletMixin {
@Shadow
private MinecraftClient client;

@Inject(method = "init", at = @At("HEAD"))
public void preInit(CallbackInfo ci) {
try {
//noinspection JavaReflectionMemberAccess
Field f = GameInstanceUtils.class.getField("APPLET");
f.setAccessible(true);
f.set(null, this);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError();
}
}

@Inject(method = "init", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/MinecraftClient;field_969:Z"))
public void afterInit(CallbackInfo ci) {
try {
//noinspection JavaReflectionMemberAccess
Field f = GameInstanceUtils.class.getField("CLIENT");
f.setAccessible(true);
f.set(null, this.client);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError();
}
}
}
5 changes: 4 additions & 1 deletion textile-api-base/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@
"depends": {
"fabricloader": ">=0.4.0"
},
"description": "Contains the essentials for Textile API modules."
"description": "Contains the essentials for Textile API modules.",
"mixins": [
"textile-api-base.mixins.json"
]
}
11 changes: 11 additions & 0 deletions textile-api-base/src/main/resources/textile-api-base.mixins.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"required": true,
"package": "net.textilemc.textile.mixin.base",
"compatibilityLevel": "JAVA_8",
"mixins": [
"MinecraftAppletMixin"
],
"injectors": {
"defaultRequire": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package net.textilemc.textile.api.recipe.v1;

import java.util.Arrays;
import java.util.List;

import net.textilemc.textile.mixin.recipe.CraftingInventoryAccessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import net.minecraft.item.ItemStack;
import net.minecraft.storage.CraftingInventory;
import net.minecraft.world.World;

/**
* Base class for custom crafting recipe types.
*/
public interface CraftingRecipeType {
/**
* @param inventory The inventory. May be null
* @return Output stack
*/
ItemStack getOutput(List<ItemStack> inventory);

/**
* @param inventory The inventory
* @return Whether the inventory's stacks match
*/
default boolean matches(CraftingInventory inventory, World world) {
//noinspection ConstantConditions
return this.matches(Arrays.asList(((CraftingInventoryAccessor) (Object) inventory).getField_871().clone()), world);
}

/**
* @param stacks The item stacks
* @return Whether the inventory's stacks match
*/
boolean matches(List<ItemStack> stacks, World world);

/**
* Called after this recipe has been matched and consumed.
*
* <p>This can be used to add recipe remainders, damage items, etc.</p>
*/
default void afterEvaluate(@NotNull CraftingInventory inventory, World world) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.textilemc.textile.api.recipe.v1;

import java.util.Collections;
import java.util.List;

import net.textilemc.textile.impl.recipe.DispatcherHolder;

import net.minecraft.item.ItemStack;

/**
* Stores a bunch of recipes
*/
public interface Dispatcher<T extends CraftingRecipeType> {
void accept(T recipe);

ItemStack getOutput(List<ItemStack> stacks);

static <T extends CraftingRecipeType> void register(Dispatcher<T> dispatcher) {
DispatcherHolder.DISPATCHERS.add(dispatcher);
}

static List<Dispatcher<?>> getAll() {
return Collections.unmodifiableList(DispatcherHolder.DISPATCHERS);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package net.textilemc.textile.api.recipe.v1;

import java.util.ArrayList;
import java.util.List;

import com.google.common.base.Preconditions;

import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;

/**
* Recipe type for recipes that do not have any specific shape.
*/
public class ShapelessRecipe implements CraftingRecipeType {
public static final Dispatcher<ShapelessRecipe> DISPATCHER = new SimpleDispatcher<>();
private final ItemStack output;
private final ArrayList<ItemStack> ingredients;

/**
* @param output The output stack
* @param ingredients The ingredients
* @throws IllegalArgumentException If the size of ingredients is 0
*/
public ShapelessRecipe(ItemStack output, ArrayList<ItemStack> ingredients) {
Preconditions.checkArgument(ingredients.size() > 0);
this.output = output;
this.ingredients = ingredients;
}

@Override
public ItemStack getOutput(List<ItemStack> stacks) {
return this.output;
}
@Override
public boolean matches(List<ItemStack> items, World world) {
//noinspection unchecked
ArrayList<ItemStack> stacks = (ArrayList<ItemStack>) this.ingredients.clone();

for (ItemStack stack : items) {
for (ItemStack itemStack : stacks) {
if (stack != null && stack.id == itemStack.id && (itemStack.meta == 32767 || stack.meta == itemStack.meta)) {
stacks.remove(itemStack);
break;
}
}
}

return stacks.isEmpty();
}

public static Builder builder(ItemStack output) {
return new ShapelessRecipeBuilder(output);
}

static {
Dispatcher.register(DISPATCHER);
}

/**
* Helper class for creating shapeless recipes
*/
public interface Builder {
/**
* @param stack The item stack to be added to the shapeless recipe
* @return This builder
*/
Builder add(ItemStack stack);

/**
* @param item The item to be added to the shapeless recipe
* @return This builder
*/
Builder add(Item item);

/**
* Converts this builder to a recipe
*
* @return a shapeless recipe
*/
ShapelessRecipe build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package net.textilemc.textile.api.recipe.v1;

import java.util.ArrayList;
import java.util.Objects;

import com.google.common.base.Preconditions;

import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;

public class ShapelessRecipeBuilder implements ShapelessRecipe.Builder {
private final ItemStack output;
private final ArrayList<ItemStack> stacks = new ArrayList<>();

ShapelessRecipeBuilder(ItemStack output) {
this.output = output;
}

public ShapelessRecipeBuilder add(ItemStack stack) {
this.stacks.add(Objects.requireNonNull(stack));
return this;
}

public ShapelessRecipeBuilder add(Item item) {
return this.add(new ItemStack(Objects.requireNonNull(item)));
}

@SuppressWarnings("unchecked")
public ShapelessRecipe build() {
Preconditions.checkArgument(this.stacks.size() > 0, "Empty Shapeless Recipe");
return new ShapelessRecipe(this.output, (ArrayList<ItemStack>) this.stacks.clone());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package net.textilemc.textile.api.recipe.v1;

import java.util.ArrayList;
import java.util.List;

import net.textilemc.textile.api.util.GameInstanceUtils;
import org.jetbrains.annotations.Nullable;

import net.minecraft.item.ItemStack;

/**
* A simple dispatcher implementation.
* @param <T> The recipe type
*/
public class SimpleDispatcher<T extends CraftingRecipeType> implements Dispatcher<T> {
private final List<T> recipes = new ArrayList<>();

@Override
public void accept(T recipe) {
this.recipes.add(recipe);
}

@Nullable
@Override
public ItemStack getOutput(List<ItemStack> stacks) {
for (T recipe : this.recipes) {
if (recipe.matches(stacks, GameInstanceUtils.getMinecraftClient().world)) {
return recipe.getOutput(stacks);
}
}

return null;
}
}
Loading