Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Commit f583ac8

Browse files
authored
Forge Capabilities API
Forge Capabilities API
2 parents 13028d2 + a2df146 commit f583ac8

33 files changed

Lines changed: 2036 additions & 0 deletions

patchwork-capabilities/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## Reasoning
2+
MinecraftForge offers a capability API
3+
4+
## TODO
5+
* Move the `net.minecraftforge.common.util` package to another sub-project
6+
* Still need to implement special cases for
7+
* [ChestBlockEntity](https://github.com/PatchworkMC/YarnForge/blob/04d384add800bc395f4934507721f72eb733389f/patches/minecraft/net/minecraft/block/entity/ChestBlockEntity.java.patch#L46-L55)
8+
* [LockableContainerBlockEntity](https://github.com/PatchworkMC/YarnForge/blob/04d384add800bc395f4934507721f72eb733389f/patches/minecraft/net/minecraft/block/entity/LockableContainerBlockEntity.java.patch#L13-L19)
9+
* [AbstractFurnaceBlockEntity](https://github.com/PatchworkMC/YarnForge/blob/04d384add800bc395f4934507721f72eb733389f/patches/minecraft/net/minecraft/block/entity/AbstractFurnaceBlockEntity.java.patch#L123-L134)
10+
* [BrewingStandBlockEntity](https://github.com/PatchworkMC/YarnForge/blob/04d384add800bc395f4934507721f72eb733389f/patches/minecraft/net/minecraft/block/entity/BrewingStandBlockEntity.java.patch#L67-L78)
11+
* [LivingEntity](https://github.com/PatchworkMC/YarnForge/blob/04d384add800bc395f4934507721f72eb733389f/patches/minecraft/net/minecraft/entity/LivingEntity.java.patch#L512-L520)
12+
* [HorseBaseEntity](https://github.com/PatchworkMC/YarnForge/blob/04d384add800bc395f4934507721f72eb733389f/patches/minecraft/net/minecraft/entity/passive/HorseBaseEntity.java.patch#L43-L48)
13+
* [StorageMinecartEntity](https://github.com/PatchworkMC/YarnForge/blob/04d384add800bc395f4934507721f72eb733389f/patches/minecraft/net/minecraft/entity/vehicle/StorageMinecartEntity.java.patch#L44-L49)
14+
* [PlayerEntity](https://github.com/PatchworkMC/YarnForge/blob/04d384add800bc395f4934507721f72eb733389f/patches/minecraft/net/minecraft/entity/player/PlayerEntity.java.patch#L521-L529)
15+
16+
## Patcher
17+
`CapabilityInject` on methods require a `CapabilityRegisteredCallback` to be registered. `CapabilityInject` for fields
18+
requires a bridge callback method to be created, where the field is assigned from.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
archivesBaseName = "patchwork-capabilities"
2+
version = getSubprojectVersion(project, "0.1.0")
3+
4+
dependencies {
5+
compile project(path: ':patchwork-fml', configuration: 'dev')
6+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package com.patchworkmc.api.capability;
21+
22+
import net.minecraftforge.common.capabilities.Capability;
23+
24+
import net.fabricmc.fabric.api.event.Event;
25+
26+
import com.patchworkmc.impl.capability.CapabilityRegisteredCallbackInternal;
27+
28+
public interface CapabilityRegisteredCallback<C> {
29+
void onCapabilityRegistered(Capability<C> capability);
30+
31+
static <C> Event<CapabilityRegisteredCallback<C>> event(Class<C> type) {
32+
return CapabilityRegisteredCallbackInternal.getOrCreateEvent(type);
33+
}
34+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package com.patchworkmc.impl.capability;
21+
22+
import javax.annotation.Nullable;
23+
24+
import net.minecraftforge.common.MinecraftForge;
25+
import net.minecraftforge.common.capabilities.CapabilityDispatcher;
26+
import net.minecraftforge.common.capabilities.CapabilityProvider;
27+
import net.minecraftforge.common.capabilities.ICapabilityProvider;
28+
import net.minecraftforge.event.AttachCapabilitiesEvent;
29+
30+
public class BaseCapabilityProvider<T> extends CapabilityProvider<T> {
31+
private final T provider;
32+
33+
public BaseCapabilityProvider(Class<T> baseClass, T provider) {
34+
super(baseClass);
35+
this.provider = provider;
36+
}
37+
38+
@Override
39+
public void gatherCapabilities(@Nullable ICapabilityProvider parent) {
40+
AttachCapabilitiesEvent<T> event = new AttachCapabilitiesEvent<>(baseClass, provider);
41+
MinecraftForge.EVENT_BUS.post(event);
42+
43+
if (!event.getCapabilities().isEmpty() || parent != null) {
44+
capabilities = new CapabilityDispatcher(event.getCapabilities(), event.getListeners(), parent);
45+
} else {
46+
capabilities = null;
47+
}
48+
}
49+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package com.patchworkmc.impl.capability;
21+
22+
import javax.annotation.Nonnull;
23+
import javax.annotation.Nullable;
24+
25+
import net.minecraftforge.common.capabilities.Capability;
26+
import net.minecraftforge.common.capabilities.CapabilityDispatcher;
27+
import net.minecraftforge.common.capabilities.CapabilityProvider;
28+
import net.minecraftforge.common.capabilities.ICapabilityProvider;
29+
import net.minecraftforge.common.util.LazyOptional;
30+
31+
import net.minecraft.nbt.CompoundTag;
32+
import net.minecraft.util.math.Direction;
33+
34+
/**
35+
* A holder for {@link CapabilityProvider}, since some classes cannot directly extend {@link CapabilityProvider}.
36+
*/
37+
public interface CapabilityProviderHolder extends ICapabilityProvider {
38+
@Nonnull
39+
CapabilityProvider<?> getCapabilityProvider();
40+
41+
default void gatherCapabilities() {
42+
getCapabilityProvider().gatherCapabilities();
43+
}
44+
45+
default void gatherCapabilities(@Nullable ICapabilityProvider parent) {
46+
getCapabilityProvider().gatherCapabilities(parent);
47+
}
48+
49+
default CapabilityDispatcher getCapabilities() {
50+
return getCapabilityProvider().getCapabilities();
51+
}
52+
53+
default boolean areCapsCompatible(CapabilityProvider<?> other) {
54+
return getCapabilityProvider().areCapsCompatible((CapabilityProvider) other);
55+
}
56+
57+
default boolean areCapsCompatible(CapabilityDispatcher other) {
58+
return getCapabilityProvider().areCapsCompatible(other);
59+
}
60+
61+
@Nullable
62+
default CompoundTag serializeCaps() {
63+
return getCapabilityProvider().serializeCaps();
64+
}
65+
66+
default void deserializeCaps(CompoundTag tag) {
67+
getCapabilityProvider().deserializeCaps(tag);
68+
}
69+
70+
default void invalidateCaps() {
71+
getCapabilityProvider().invalidateCaps();
72+
}
73+
74+
default void reviveCaps() {
75+
getCapabilityProvider().reviveCaps();
76+
}
77+
78+
@Nonnull
79+
default <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
80+
return getCapabilityProvider().getCapability(cap, side);
81+
}
82+
83+
@Nonnull
84+
default <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap) {
85+
return getCapabilityProvider().getCapability(cap);
86+
}
87+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package com.patchworkmc.impl.capability;
21+
22+
import java.util.IdentityHashMap;
23+
import java.util.Map;
24+
25+
import org.apache.logging.log4j.LogManager;
26+
import org.apache.logging.log4j.Logger;
27+
28+
import net.fabricmc.fabric.api.event.Event;
29+
import net.fabricmc.fabric.api.event.EventFactory;
30+
31+
import com.patchworkmc.api.capability.CapabilityRegisteredCallback;
32+
33+
public class CapabilityRegisteredCallbackInternal {
34+
private static final Logger LOGGER = LogManager.getLogger(CapabilityRegisteredCallback.class);
35+
36+
private static final Map<Class<?>, Event<?>> CALLBACKS = new IdentityHashMap<>();
37+
38+
@SuppressWarnings("unchecked")
39+
public static <C> Event<CapabilityRegisteredCallback<C>> getOrCreateEvent(Class<C> type) {
40+
return (Event<CapabilityRegisteredCallback<C>>) CALLBACKS.computeIfAbsent(type, $ -> EventFactory.createArrayBacked(CapabilityRegisteredCallback.class, capability -> { }, callbacks -> capability -> {
41+
boolean error = false;
42+
Throwable throwable = new Throwable();
43+
44+
for (CapabilityRegisteredCallback<C> callback : callbacks) {
45+
try {
46+
callback.onCapabilityRegistered(capability);
47+
} catch (Throwable thr) {
48+
error = true;
49+
throwable.addSuppressed(thr);
50+
}
51+
}
52+
53+
if (error) {
54+
LOGGER.error("An uncaught exception was thrown while processing a CapabilityRegisteredCallback<{}>", type.getName(), throwable);
55+
}
56+
}));
57+
}
58+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2020, 2019-2020
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation version 2.1
8+
* of the License.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this library; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
package com.patchworkmc.mixin.capability;
21+
22+
import javax.annotation.Nonnull;
23+
24+
import net.minecraftforge.common.capabilities.CapabilityProvider;
25+
import org.spongepowered.asm.mixin.Mixin;
26+
import org.spongepowered.asm.mixin.injection.At;
27+
import org.spongepowered.asm.mixin.injection.Inject;
28+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
29+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
30+
31+
import net.minecraft.block.entity.BlockEntity;
32+
import net.minecraft.nbt.CompoundTag;
33+
34+
import com.patchworkmc.impl.capability.BaseCapabilityProvider;
35+
import com.patchworkmc.impl.capability.CapabilityProviderHolder;
36+
37+
@Mixin(BlockEntity.class)
38+
public class BlockEntityMixin implements CapabilityProviderHolder {
39+
private final CapabilityProvider<BlockEntity> provider = new BaseCapabilityProvider<>(BlockEntity.class, (BlockEntity) (Object) this);
40+
41+
@Nonnull
42+
@Override
43+
public CapabilityProvider<BlockEntity> getCapabilityProvider() {
44+
return provider;
45+
}
46+
47+
@Inject(method = "<init>", at = @At("RETURN"))
48+
private void initializeCapabilities(CallbackInfo callbackInfo) {
49+
gatherCapabilities();
50+
}
51+
52+
@Inject(method = "writeIdentifyingData", at = @At("RETURN"))
53+
private void serializeCapabilities(CompoundTag compoundTag, CallbackInfoReturnable<CompoundTag> callbackInfoReturnable) {
54+
if (getCapabilities() != null) {
55+
compoundTag.put("ForgeCaps", serializeCaps());
56+
}
57+
}
58+
59+
@Inject(method = "fromTag", at = @At("RETURN"))
60+
private void deserializeCapabilities(CompoundTag compoundTag, CallbackInfo callbackInfo) {
61+
if (getCapabilities() != null && compoundTag.containsKey("ForgeCaps")) {
62+
deserializeCaps(compoundTag.getCompound("ForgeCaps"));
63+
}
64+
}
65+
66+
@Inject(method = "invalidate", at = @At("RETURN"))
67+
private void invalidate(CallbackInfo callbackInfo) {
68+
invalidateCaps();
69+
}
70+
}

0 commit comments

Comments
 (0)