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

Commit b4f7730

Browse files
authored
Merge pull request #11 from TheGlitch76/feature/imc
Inter Mod Communication
2 parents ec90709 + 1be31f2 commit b4f7730

6 files changed

Lines changed: 330 additions & 3 deletions

File tree

patchwork-dispatcher/src/main/java/com/patchworkmc/impl/Patchwork.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import net.minecraftforge.fml.ModLoadingContext;
3232
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
3333
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent;
34+
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
35+
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
3436
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
3537
import net.minecraftforge.fml.javafmlmod.FMLModContainer;
3638
import net.minecraftforge.registries.ForgeRegistry;
@@ -115,10 +117,12 @@ public void onInitialize() {
115117
}
116118

117119
// Send initialization events
118-
119120
dispatchRegistryEvents(mods);
120-
dispatch(mods, new FMLCommonSetupEvent(new ModContainer("minecraft"))); // TODO: One per modcontainer
121-
dispatch(mods, new FMLLoadCompleteEvent(new ModContainer("minecraft"))); // TODO: Ditto
121+
// TODO: One per modcontainer
122+
dispatch(mods, new FMLCommonSetupEvent(new ModContainer("minecraft")));
123+
dispatch(mods, new InterModEnqueueEvent(new ModContainer("minecraft")));
124+
dispatch(mods, new InterModProcessEvent(new ModContainer("minecraft")));
125+
dispatch(mods, new FMLLoadCompleteEvent(new ModContainer("minecraft")));
122126

123127
MinecraftForge.EVENT_BUS.start();
124128
}

patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLFingerprintViolationEvent.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ public class FMLFingerprintViolationEvent extends ModLifecycleEvent {
3636
private final File source;
3737
private final String expectedFingerprint;
3838

39+
// For EventBus
40+
public FMLFingerprintViolationEvent() {
41+
super();
42+
this.isDirectory = false;
43+
this.fingerprints = null;
44+
this.source = null;
45+
this.expectedFingerprint = null;
46+
}
47+
3948
public FMLFingerprintViolationEvent(boolean isDirectory, File source, ImmutableSet<String> fingerprints, String expectedFingerprint) {
4049
super(null);
4150
this.isDirectory = isDirectory;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2019, 2019
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 net.minecraftforge.fml.event.lifecycle;
21+
22+
import net.minecraftforge.fml.ModContainer;
23+
24+
/**
25+
* This is the third of four commonly called events during mod lifecycle startup.
26+
*
27+
* <p>Called before {@link InterModProcessEvent}</p>
28+
*
29+
* <p>Called after {@link FMLClientSetupEvent} or {@link FMLDedicatedServerSetupEvent}</p>
30+
*
31+
* <p>Enqueue {@link net.minecraftforge.fml.InterModComms} messages to other mods with this event.</p>
32+
*
33+
* <p>This is a parallel dispatch event.</p>
34+
*/
35+
public class InterModEnqueueEvent extends ModLifecycleEvent {
36+
// For EventBus
37+
public InterModEnqueueEvent() {
38+
super();
39+
}
40+
41+
public InterModEnqueueEvent(final ModContainer container) {
42+
super(container);
43+
}
44+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2019, 2019
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 net.minecraftforge.fml.event.lifecycle;
21+
22+
import java.util.function.Predicate;
23+
24+
import net.minecraftforge.fml.ModContainer;
25+
26+
/**
27+
* This is the fourth of four commonly called events during mod lifecycle startup.
28+
*
29+
* <p>Called after {@link InterModEnqueueEvent}</p>
30+
*
31+
* <p>Retrieve {@link net.minecraftforge.fml.InterModComms} {@link net.minecraftforge.fml.InterModComms.IMCMessage} suppliers
32+
* and process them as you wish with this event.</p>
33+
*
34+
* <p>This is a parallel dispatch event.</p>
35+
*
36+
* @see #getIMCStream()
37+
* @see #getIMCStream(Predicate)
38+
*/
39+
public class InterModProcessEvent extends ModLifecycleEvent {
40+
// For EventBus
41+
public InterModProcessEvent() {
42+
super();
43+
}
44+
45+
public InterModProcessEvent(final ModContainer container) {
46+
super(container);
47+
}
48+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2019, 2019
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 net.minecraftforge.fml;
21+
22+
import java.util.Iterator;
23+
import java.util.Spliterator;
24+
import java.util.concurrent.ConcurrentHashMap;
25+
import java.util.concurrent.ConcurrentLinkedQueue;
26+
import java.util.concurrent.ConcurrentMap;
27+
import java.util.function.Consumer;
28+
import java.util.function.Predicate;
29+
import java.util.function.Supplier;
30+
import java.util.stream.Stream;
31+
import java.util.stream.StreamSupport;
32+
33+
public class InterModComms {
34+
public static final class IMCMessage {
35+
private final String modId;
36+
private final String method;
37+
private final String senderModId;
38+
private final Supplier<?> thing;
39+
40+
IMCMessage(String senderModId, String modId, String method, Supplier<?> thing) {
41+
this.senderModId = senderModId;
42+
this.modId = modId;
43+
this.method = method;
44+
this.thing = thing;
45+
}
46+
47+
/**
48+
* @return The modid of the sender. This is supplied by the caller, or by the active mod container context.
49+
* Consider it unreliable.
50+
*/
51+
public String getSenderModId() {
52+
return this.senderModId;
53+
}
54+
55+
/**
56+
* @return The modid being sent to.
57+
*/
58+
public String getModId() {
59+
return this.modId;
60+
}
61+
62+
/**
63+
* @return The method being sent to.
64+
*/
65+
public String getMethod() {
66+
return this.method;
67+
}
68+
69+
/**
70+
* @param <T> The type of the message.
71+
* @return A {@link Supplier} of the message.
72+
*/
73+
@SuppressWarnings("unchecked")
74+
public <T> Supplier<T> getMessageSupplier() {
75+
return (Supplier<T>) this.thing;
76+
}
77+
}
78+
79+
private static ConcurrentMap<String, ConcurrentLinkedQueue<IMCMessage>> containerQueues = new ConcurrentHashMap<>();
80+
81+
/**
82+
* Send IMC to remote. Sender will default to the active modcontainer, or minecraft if not.
83+
*
84+
* @param modId the mod id to send to
85+
* @param method the method name to send
86+
* @param thing the thing associated with the method name
87+
* @return true if the message was enqueued for sending (the target modid is loaded)
88+
*/
89+
public static boolean sendTo(final String modId, final String method, final Supplier<?> thing) {
90+
if (!ModList.get().isLoaded(modId)) {
91+
return false;
92+
}
93+
94+
containerQueues.computeIfAbsent(modId, k -> new ConcurrentLinkedQueue<>()).add(new IMCMessage(ModLoadingContext.get().getActiveContainer().getModId(), modId, method, thing));
95+
return true;
96+
}
97+
98+
/**
99+
* Send IMC to remote.
100+
*
101+
* @param senderModId the mod id you are sending from
102+
* @param modId the mod id to send to
103+
* @param method the method name to send
104+
* @param thing the thing associated with the method name
105+
* @return true if the message was enqueued for sending (the target modid is loaded)
106+
*/
107+
public static boolean sendTo(final String senderModId, final String modId, final String method, final Supplier<?> thing) {
108+
if (!ModList.get().isLoaded(modId)) {
109+
return false;
110+
}
111+
112+
containerQueues.computeIfAbsent(modId, k -> new ConcurrentLinkedQueue<>()).add(new IMCMessage(senderModId, modId, method, thing));
113+
return true;
114+
}
115+
116+
/**
117+
* Retrieve pending messages for your modid. Use the predicate to filter the method name.
118+
*
119+
* @param modId the modid you are querying for
120+
* @param methodMatcher a predicate for the method you are interested in
121+
* @return All messages passing the supplied method predicate
122+
*/
123+
public static Stream<IMCMessage> getMessages(final String modId, final Predicate<String> methodMatcher) {
124+
ConcurrentLinkedQueue<IMCMessage> queue = containerQueues.get(modId);
125+
126+
if (queue == null) {
127+
return Stream.empty();
128+
}
129+
130+
return StreamSupport.stream(new QueueFilteringSpliterator(queue, methodMatcher), false);
131+
}
132+
133+
/**
134+
* Retrieve all message for your modid.
135+
*
136+
* @param modId the modid you are querying for
137+
* @return All messages
138+
*/
139+
public static Stream<IMCMessage> getMessages(final String modId) {
140+
return getMessages(modId, s -> Boolean.TRUE);
141+
}
142+
143+
private static class QueueFilteringSpliterator implements Spliterator<IMCMessage> {
144+
private final ConcurrentLinkedQueue<IMCMessage> queue;
145+
private final Predicate<String> methodFilter;
146+
private final Iterator<IMCMessage> iterator;
147+
148+
private QueueFilteringSpliterator(final ConcurrentLinkedQueue<IMCMessage> queue, final Predicate<String> methodFilter) {
149+
this.queue = queue;
150+
this.iterator = queue.iterator();
151+
this.methodFilter = methodFilter;
152+
}
153+
154+
@Override
155+
public int characteristics() {
156+
return Spliterator.CONCURRENT | Spliterator.NONNULL | Spliterator.ORDERED;
157+
}
158+
159+
@Override
160+
public long estimateSize() {
161+
return queue.size();
162+
}
163+
164+
@Override
165+
public boolean tryAdvance(final Consumer<? super IMCMessage> action) {
166+
if (!iterator.hasNext()) {
167+
return false;
168+
}
169+
170+
IMCMessage next = iterator.next();
171+
172+
while (iterator.hasNext() && !methodFilter.test(next.method)) {
173+
next = iterator.next();
174+
}
175+
176+
action.accept(next);
177+
this.iterator.remove();
178+
return true;
179+
}
180+
181+
@Override
182+
public Spliterator<IMCMessage> trySplit() {
183+
throw new UnsupportedOperationException("forge behavior is return null, report this to Patchwork!");
184+
}
185+
}
186+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Minecraft Forge, Patchwork Project
3+
* Copyright (c) 2016-2019, 2019
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 net.minecraftforge.fml;
21+
22+
import net.fabricmc.loader.FabricLoader;
23+
24+
public class ModList {
25+
// Patchwork: initalize directly because there's no args
26+
private static ModList INSTANCE = new ModList();
27+
28+
public static ModList get() {
29+
return INSTANCE;
30+
}
31+
32+
public boolean isLoaded(String modId) {
33+
// Patchwork: use Fabric Loader lookup instead of an internal one
34+
return FabricLoader.INSTANCE.isModLoaded(modId);
35+
}
36+
}

0 commit comments

Comments
 (0)