commit 5a39747a658cfd3837fefb52139147f3aa7bc9ba Author: shedaniel Date: Sun Nov 1 19:59:17 2020 +0800 Initial work diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..6f666f8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +build/ +*.ipr +run/ +*.iws +out/ +*.iml +.gradle/ +output/ +bin/ +libs/ + +.classpath +.project +.settings/org.eclipse.core.resources.prefs +.idea/ +classes/ \ No newline at end of file diff --git a/HEADER b/HEADER new file mode 100644 index 00000000..2652fe71 --- /dev/null +++ b/HEADER @@ -0,0 +1,13 @@ +Copyright ${year} ${name} + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..3e2f4b38 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,170 @@ +Apache License +============== + +_Version 2.0, January 2004_ +_<>_ + +### Terms and Conditions for use, reproduction, and distribution + +#### 1. Definitions + +“License” shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +“Licensor” shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +“Legal Entity” shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, “control” means **(i)** the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the +outstanding shares, or **(iii)** beneficial ownership of such entity. + +“You” (or “Your”) shall mean an individual or Legal Entity exercising +permissions granted by this License. + +“Source” form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +“Object” form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +“Work” shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +“Derivative Works” shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +“Contribution” shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +“submitted” means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as “Not a Contribution.” + +“Contributor” shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +#### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +#### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +#### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +* **(a)** You must give any other recipients of the Work or Derivative Works a copy of +this License; and +* **(b)** You must cause any modified files to carry prominent notices stating that You +changed the files; and +* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +#### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +#### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +#### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +#### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +#### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +_END OF TERMS AND CONDITIONS_ diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..63a2ff44 --- /dev/null +++ b/build.gradle @@ -0,0 +1,36 @@ +plugins { + id "architect-plugin" version "1.0.10" + id "org.cadixdev.licenser" version "0.5.0" +} + +architect { + minecraft = rootProject.minecraft_version +} + +allprojects { + apply plugin: "java" + apply plugin: "architect-plugin" + apply plugin: "org.cadixdev.licenser" + + archivesBaseName = rootProject.archives_base_name + version = rootProject.mod_version + group = rootProject.maven_group + + tasks.withType(JavaCompile) { + options.encoding = "UTF-8" + } + + license { + header = rootProject.file("HEADER") + + ext { + name = "shedaniel" + year = 2020 + } + + ignoreFailures = true + } +} + +task licenseFormatAll +subprojects { p -> licenseFormatAll.dependsOn("${p.path}:licenseFormat") } \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 00000000..7c8cbb77 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,16 @@ +plugins { + id "fabric-loom" +} + +dependencies { + minecraft "com.mojang:minecraft:${rootProject.architect.minecraft}" + mappings minecraft.officialMojangMappings() + // We depend on fabric loader here to use the fabric @Environment annotations + // Do NOT use other classes from fabric loader + modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + implementation "net.jodah:typetools:0.6.2" +} + +architect { + common() +} \ No newline at end of file diff --git a/common/src/main/java/me/shedaniel/architectury/Architectury.java b/common/src/main/java/me/shedaniel/architectury/Architectury.java new file mode 100644 index 00000000..6f37b4f5 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/Architectury.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury; + +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public class Architectury { + private static final String MOD_LOADER; + + public static String getModLoader() { + return MOD_LOADER; + } + + static { + String loader; + try { + Class.forName("net.fabricmc.loader.FabricLoader"); + loader = "fabric"; + } catch (ClassNotFoundException e) { + loader = "forge"; + } + MOD_LOADER = loader; + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/ArchitecturyPopulator.java b/common/src/main/java/me/shedaniel/architectury/ArchitecturyPopulator.java new file mode 100644 index 00000000..e8096da3 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/ArchitecturyPopulator.java @@ -0,0 +1,57 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury; + +import org.apache.commons.lang3.reflect.FieldUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public final class ArchitecturyPopulator { + private ArchitecturyPopulator() {} + + public static void populate(Object o) { + try { + if (o instanceof Class) { + Class aClass = (Class) o; + for (Field field : aClass.getDeclaredFields()) { + if (!field.isAnnotationPresent(Populatable.class)) continue; + if (Modifier.isStatic(field.getModifiers())) { + FieldUtils.removeFinalModifier(field); + field.setAccessible(true); + String type = field.getType().toString().replace("$", ""); + Class newClass = Class.forName(type.substring(0, type.lastIndexOf('.')) + "." + Architectury.getModLoader() + "." + type.substring(type.lastIndexOf('.') + 1)); + field.set(null, newClass.getConstructor().newInstance()); + } + } + } else { + for (Field field : o.getClass().getDeclaredFields()) { + if (!field.isAnnotationPresent(Populatable.class)) continue; + if (!Modifier.isStatic(field.getModifiers())) { + FieldUtils.removeFinalModifier(field); + field.setAccessible(true); + String type = field.getType().toString().replace("$", ""); + Class newClass = Class.forName(type.substring(0, type.lastIndexOf('.')) + "." + Architectury.getModLoader() + "." + type.substring(type.lastIndexOf('.') + 1)); + field.set(o, newClass.getConstructor().newInstance()); + } + } + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/Populatable.java b/common/src/main/java/me/shedaniel/architectury/Populatable.java new file mode 100644 index 00000000..6aed4a22 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/Populatable.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Populatable { +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/Event.java b/common/src/main/java/me/shedaniel/architectury/event/Event.java new file mode 100644 index 00000000..e5453cb3 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/Event.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event; + +public interface Event { + T invoker(); + + void register(T listener); + + void unregister(T listener); + + boolean isRegistered(T listener); + + void clearListeners(); +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java b/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java new file mode 100644 index 00000000..612572ef --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/EventFactory.java @@ -0,0 +1,150 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event; + +import com.google.common.reflect.AbstractInvocationHandler; +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import me.shedaniel.architectury.platform.Platform; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.jodah.typetools.TypeResolver; +import net.minecraft.world.InteractionResult; +import org.apache.commons.lang3.ArrayUtils; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Objects; +import java.util.function.Function; + +public final class EventFactory { + private EventFactory() {} + + @Populatable + private static final Impl IMPL = null; + + public static Event create(Function function) { + Class[] arguments = TypeResolver.resolveRawArguments(Function.class, function.getClass()); + return new EventImpl<>(arguments[1], function); + } + + @SuppressWarnings("UnstableApiUsage") + public static Event createLoop(Class clazz) { + return create(ts -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() { + @Override + protected Object handleInvocation(Object proxy, Method method, Object[] args) { + return null; + } + })); + } + + @SuppressWarnings("UnstableApiUsage") + public static Event createInteractionResult(Class clazz) { + return create(ts -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() { + @Override + protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable { + T[] listeners = (T[]) args; + for (T listener : listeners) { + InteractionResult result = (InteractionResult) method.invoke(listener, args); + if (result != InteractionResult.PASS) { + return result; + } + } + return InteractionResult.PASS; + } + })); + } + + private static class EventImpl implements Event { + private final Function function; + private T invoker = null; + private T[] listeners; + private Class clazz; + + public EventImpl(Class clazz, Function function) { + this.clazz = Objects.requireNonNull(clazz); + this.function = function; + this.listeners = emptyArray(); + update(); + } + + private T[] emptyArray() { + try { + return (T[]) Array.newInstance(clazz, 0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public T invoker() { + return invoker; + } + + @Override + public void register(T listener) { + listeners = ArrayUtils.add(listeners, listener); + update(); + } + + @Override + public void unregister(T listener) { + listeners = ArrayUtils.removeElement(listeners, listener); + update(); + } + + @Override + public boolean isRegistered(T listener) { + return ArrayUtils.contains(listeners, listener); + } + + @Override + public void clearListeners() { + listeners = emptyArray(); + update(); + } + + public void update() { + if (listeners.length == 1) { + invoker = listeners[0]; + } else { + invoker = function.apply(listeners); + } + } + } + + public interface Impl { + @Environment(EnvType.CLIENT) + void registerClient(); + + void registerCommon(); + + @Environment(EnvType.SERVER) + void registerServer(); + } + + static { + ArchitecturyPopulator.populate(EventFactory.class); + if (Platform.getEnv() == EnvType.CLIENT) + IMPL.registerClient(); + IMPL.registerCommon(); + if (Platform.getEnv() == EnvType.SERVER) + IMPL.registerServer(); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java new file mode 100644 index 00000000..338d15a0 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.events; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.architectury.event.Event; +import me.shedaniel.architectury.event.EventFactory; + +public interface GuiEvent { + Event RENDER_HUD = EventFactory.createLoop(RenderHud.class); + + interface RenderHud { + void renderHud(PoseStack matrices, float tickDelta); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java new file mode 100644 index 00000000..c9183bf9 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java @@ -0,0 +1,44 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.events; + +import me.shedaniel.architectury.event.Event; +import me.shedaniel.architectury.event.EventFactory; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.server.MinecraftServer; + +public interface LifecycleEvent { + @Environment(EnvType.CLIENT) + Event CLIENT_STARTED = EventFactory.createLoop(ClientState.class); + @Environment(EnvType.CLIENT) + Event CLIENT_STOPPING = EventFactory.createLoop(ClientState.class); + Event SERVER_STARTING = EventFactory.createLoop(ServerState.class); + Event SERVER_STARTED = EventFactory.createLoop(ServerState.class); + Event SERVER_STOPPING = EventFactory.createLoop(ServerState.class); + Event SERVER_STOPPED = EventFactory.createLoop(ServerState.class); + + interface InstanceState { + void stateChanged(T instance); + } + + @Environment(EnvType.CLIENT) + interface ClientState extends InstanceState {} + + interface ServerState extends InstanceState {} +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java new file mode 100644 index 00000000..310c879a --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java @@ -0,0 +1,56 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.events; + +import me.shedaniel.architectury.event.Event; +import me.shedaniel.architectury.event.EventFactory; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; + +public interface TickEvent { + @Environment(EnvType.CLIENT) + Event CLIENT_PRE = EventFactory.createLoop(Client.class); + @Environment(EnvType.CLIENT) + Event CLIENT_POST = EventFactory.createLoop(Client.class); + Event SERVER_PRE = EventFactory.createLoop(Server.class); + Event SERVER_POST = EventFactory.createLoop(Server.class); + @Environment(EnvType.CLIENT) + Event CLIENT_WORLD_PRE = EventFactory.createLoop(ClientWorld.class); + @Environment(EnvType.CLIENT) + Event CLIENT_WORLD_POST = EventFactory.createLoop(ClientWorld.class); + Event SERVER_WORLD_PRE = EventFactory.createLoop(ServerWorld.class); + Event SERVER_WORLD_POST = EventFactory.createLoop(ServerWorld.class); + + void tick(T instance); + + @Environment(EnvType.CLIENT) + interface Client extends TickEvent {} + + interface Server extends TickEvent {} + + interface WorldTick extends TickEvent {} + + @Environment(EnvType.CLIENT) + interface ClientWorld extends WorldTick {} + + interface ServerWorld extends WorldTick {} +} diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/TooltipEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/TooltipEvent.java new file mode 100644 index 00000000..b4ac75a1 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/TooltipEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.events; + +import me.shedaniel.architectury.event.Event; +import me.shedaniel.architectury.event.EventFactory; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; + +import java.util.List; + +@Environment(EnvType.CLIENT) +public interface TooltipEvent { + Event ITEM = EventFactory.createLoop(Item.class); + + @Environment(EnvType.CLIENT) + interface Item { + void append(ItemStack stack, List lines, TooltipFlag flag); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java b/common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java new file mode 100644 index 00000000..61ea4ef6 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java @@ -0,0 +1,115 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.networking; + +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; + +public final class NetworkManager { + @Populatable + private static final Impl IMPL = null; + + public static void registerReceiver(Side side, ResourceLocation id, NetworkReceiver receiver) { + IMPL.registerReceiver(side, id, receiver); + } + + public static Packet toPacket(Side side, ResourceLocation id, FriendlyByteBuf buf) { + return IMPL.toPacket(side, id, buf); + } + + public static void sendToPlayer(ServerPlayer player, ResourceLocation id, FriendlyByteBuf buf) { + player.connection.send(toPacket(serverToClient(), id, buf)); + } + + public static void sendToPlayers(Iterable players, ResourceLocation id, FriendlyByteBuf buf) { + Packet packet = toPacket(serverToClient(), id, buf); + for (ServerPlayer player : players) { + player.connection.send(packet); + } + } + + @Environment(EnvType.CLIENT) + public static void sendToServer(ResourceLocation id, FriendlyByteBuf buf) { + Minecraft.getInstance().getConnection().send(toPacket(clientToServer(), id, buf)); + } + + @Environment(EnvType.CLIENT) + public static boolean canServerReceive(ResourceLocation id) { + return IMPL.canServerReceive(id); + } + + public static boolean canPlayerReceive(ServerPlayer player, ResourceLocation id) { + return IMPL.canPlayerReceive(player, id); + } + + public interface Impl { + void registerReceiver(Side side, ResourceLocation id, NetworkReceiver receiver); + + Packet toPacket(Side side, ResourceLocation id, FriendlyByteBuf buf); + + @Environment(EnvType.CLIENT) + boolean canServerReceive(ResourceLocation id); + + boolean canPlayerReceive(ServerPlayer player, ResourceLocation id); + } + + @FunctionalInterface + public interface NetworkReceiver { + void receive(FriendlyByteBuf buf, PacketContext context); + } + + public interface PacketContext { + Player getPlayer(); + + void queue(Runnable runnable); + + EnvType getEnv(); + } + + public static Side s2c() { + return Side.S2C; + } + + public static Side c2s() { + return Side.C2S; + } + + public static Side serverToClient() { + return Side.S2C; + } + + public static Side clientToServer() { + return Side.C2S; + } + + public enum Side { + S2C, + C2S + } + + static { + ArchitecturyPopulator.populate(NetworkManager.class); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/platform/Mod.java b/common/src/main/java/me/shedaniel/architectury/platform/Mod.java new file mode 100644 index 00000000..c5cea174 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/platform/Mod.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.platform; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.screens.Screen; +import org.jetbrains.annotations.NotNull; + +public interface Mod { + @NotNull + String getModId(); + + @NotNull + String getVersion(); + + @NotNull + String getName(); + + @NotNull + String getDescription(); + + @Environment(EnvType.CLIENT) + void registerConfigurationScreen(ConfigurationScreenProvider provider); + + @Environment(EnvType.CLIENT) + @FunctionalInterface + interface ConfigurationScreenProvider { + Screen provide(Screen parent); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/platform/Platform.java b/common/src/main/java/me/shedaniel/architectury/platform/Platform.java new file mode 100644 index 00000000..8ccb2684 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/platform/Platform.java @@ -0,0 +1,90 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.platform; + +import me.shedaniel.architectury.Architectury; +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import net.fabricmc.api.EnvType; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Path; +import java.util.Collection; + +public final class Platform { + private Platform() {} + + @Populatable + private static final Impl IMPL = null; + + /** + * @return the current mod loader, either "fabric" or "forge" + */ + @NotNull + public static String getModLoader() { + return Architectury.getModLoader(); + } + + @NotNull + public static Path getGameFolder() { + return IMPL.getGameFolder(); + } + + @NotNull + public static Path getConfigFolder() { + return IMPL.getConfigFolder(); + } + + @NotNull + public static EnvType getEnv() { + return IMPL.getEnv(); + } + + public static boolean isModLoaded(String id) { + return IMPL.isModLoaded(id); + } + + @NotNull + public static Mod getMod(String id) { + return IMPL.getMod(id); + } + + @NotNull + public static Collection getMods() { + return IMPL.getMods(); + } + + public interface Impl { + Path getGameFolder(); + + Path getConfigFolder(); + + Path getModsFolder(); + + EnvType getEnv(); + + boolean isModLoaded(String id); + + Mod getMod(String id); + + Collection getMods(); + } + + static { + ArchitecturyPopulator.populate(Platform.class); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/registry/Registries.java b/common/src/main/java/me/shedaniel/architectury/registry/Registries.java new file mode 100644 index 00000000..de402459 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/registry/Registries.java @@ -0,0 +1,63 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.registry; + +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import net.minecraft.resources.ResourceKey; + +import java.util.HashMap; +import java.util.Map; + +public final class Registries { + @Populatable + private static final Impl IMPL = null; + private static final Map REGISTRIES = new HashMap<>(); + private final RegistryProvider provider; + + public static Registries get(String modId) { + return REGISTRIES.computeIfAbsent(modId, Registries::new); + } + + private Registries(String modId) { + this.provider = IMPL.get(modId); + } + + public Registry get(ResourceKey> key) { + return this.provider.get(key); + } + + @Deprecated + public Registry get(net.minecraft.core.Registry registry) { + return this.provider.get(registry); + } + + public interface Impl { + RegistryProvider get(String modId); + } + + public interface RegistryProvider { + Registry get(ResourceKey> key); + + @Deprecated + Registry get(net.minecraft.core.Registry registry); + } + + static { + ArchitecturyPopulator.populate(Registries.class); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/registry/Registry.java b/common/src/main/java/me/shedaniel/architectury/registry/Registry.java new file mode 100644 index 00000000..6b99347a --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/registry/Registry.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.registry; + +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +public interface Registry { + Supplier delegate(ResourceLocation id); + + Supplier register(ResourceLocation id, Supplier supplier); + + @Nullable + ResourceLocation getId(T obj); + + @Nullable + T get(ResourceLocation id); +} diff --git a/common/src/main/java/me/shedaniel/architectury/utils/GameInstance.java b/common/src/main/java/me/shedaniel/architectury/utils/GameInstance.java new file mode 100644 index 00000000..33dda031 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/utils/GameInstance.java @@ -0,0 +1,31 @@ +package me.shedaniel.architectury.utils; + +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.server.MinecraftServer; + +public final class GameInstance { + @Populatable + private static final Impl IMPL = null; + + @Environment(EnvType.CLIENT) + public static Minecraft getClient() { + return Minecraft.getInstance(); + } + + @Environment(EnvType.SERVER) + public static MinecraftServer getServer() { + return IMPL.getServer(); + } + + public interface Impl { + MinecraftServer getServer(); + } + + static { + ArchitecturyPopulator.populate(GameInstance.class); + } +} diff --git a/common/src/main/resources/fabric.mod.json b/common/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..135630f5 --- /dev/null +++ b/common/src/main/resources/fabric.mod.json @@ -0,0 +1,6 @@ +{ + "_comment": "This file is here to make fabric loader load this on the Knot classloader.", + "schemaVersion": 1, + "id": "architectury-common", + "version": "0.0.1" +} \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle new file mode 100644 index 00000000..9764f329 --- /dev/null +++ b/fabric/build.gradle @@ -0,0 +1,33 @@ +plugins { + id "fabric-loom" + id "com.github.johnrengelman.shadow" version "5.0.0" +} + +configurations { + shadow +} + +dependencies { + minecraft("com.mojang:minecraft:${rootProject.architect.minecraft}") + mappings(minecraft.officialMojangMappings()) + modCompile("net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}") + modCompile("net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}") + modCompileOnly("io.github.prospector:modmenu:${rootProject.mod_menu_version}") + + compile(project(":common")) { + transitive = false + } + shadow(project(":common")) { + transitive = false + } +} + +shadowJar { + configurations = [project.configurations.shadow] + classifier "shadow" +} + +remapJar { + dependsOn(shadowJar) + input.set(shadowJar.archivePath) +} \ No newline at end of file diff --git a/fabric/src/main/java/me/shedaniel/architectury/compat/fabric/ModMenuCompatibility.java b/fabric/src/main/java/me/shedaniel/architectury/compat/fabric/ModMenuCompatibility.java new file mode 100644 index 00000000..207c69a2 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/compat/fabric/ModMenuCompatibility.java @@ -0,0 +1,43 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.compat.fabric; + +import com.google.common.collect.Maps; +import io.github.prospector.modmenu.api.ConfigScreenFactory; +import io.github.prospector.modmenu.api.ModMenuApi; +import me.shedaniel.architectury.platform.fabric.PlatformImpl; +import me.shedaniel.architectury.platform.Mod; + +import java.util.Map; + +public class ModMenuCompatibility implements ModMenuApi { + private static final Map> FACTORIES = Maps.newHashMap(); + + @Override + public Map> getProvidedConfigScreenFactories() { + validateMap(); + return FACTORIES; + } + + private void validateMap() { + for (Map.Entry entry : PlatformImpl.CONFIG_SCREENS.entrySet()) { + if (!FACTORIES.containsKey(entry.getKey())) { + FACTORIES.put(entry.getKey(), entry.getValue()::provide); + } + } + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java b/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java new file mode 100644 index 00000000..d9d0b14d --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.fabric; + +import me.shedaniel.architectury.event.EventFactory; +import me.shedaniel.architectury.event.events.GuiEvent; +import me.shedaniel.architectury.event.events.LifecycleEvent; +import me.shedaniel.architectury.event.events.TickEvent; +import me.shedaniel.architectury.event.events.TooltipEvent; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; + +public class EventFactoryImpl implements EventFactory.Impl { + @Override + public void registerClient() { + ClientLifecycleEvents.CLIENT_STARTED.register(LifecycleEvent.CLIENT_STARTED.invoker()::stateChanged); + ClientLifecycleEvents.CLIENT_STOPPING.register(LifecycleEvent.CLIENT_STOPPING.invoker()::stateChanged); + + ClientTickEvents.START_CLIENT_TICK.register(TickEvent.CLIENT_PRE.invoker()::tick); + ClientTickEvents.END_CLIENT_TICK.register(TickEvent.CLIENT_POST.invoker()::tick); + ClientTickEvents.START_WORLD_TICK.register(TickEvent.CLIENT_WORLD_PRE.invoker()::tick); + ClientTickEvents.END_WORLD_TICK.register(TickEvent.CLIENT_WORLD_POST.invoker()::tick); + + ItemTooltipCallback.EVENT.register((itemStack, tooltipFlag, list) -> TooltipEvent.ITEM.invoker().append(itemStack, list, tooltipFlag)); + HudRenderCallback.EVENT.register(GuiEvent.RENDER_HUD.invoker()::renderHud); + } + + @Override + public void registerCommon() { + ServerLifecycleEvents.SERVER_STARTING.register(LifecycleEvent.SERVER_STARTING.invoker()::stateChanged); + ServerLifecycleEvents.SERVER_STARTED.register(LifecycleEvent.SERVER_STARTED.invoker()::stateChanged); + ServerLifecycleEvents.SERVER_STOPPING.register(LifecycleEvent.SERVER_STOPPING.invoker()::stateChanged); + ServerLifecycleEvents.SERVER_STOPPED.register(LifecycleEvent.SERVER_STOPPED.invoker()::stateChanged); + + ServerTickEvents.START_SERVER_TICK.register(TickEvent.SERVER_PRE.invoker()::tick); + ServerTickEvents.END_SERVER_TICK.register(TickEvent.SERVER_POST.invoker()::tick); + ServerTickEvents.START_WORLD_TICK.register(TickEvent.SERVER_WORLD_PRE.invoker()::tick); + ServerTickEvents.END_WORLD_TICK.register(TickEvent.SERVER_WORLD_POST.invoker()::tick); + } + + @Override + public void registerServer() { + + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/NetworkManagerImpl.java b/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/NetworkManagerImpl.java new file mode 100644 index 00000000..0b41ac94 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/NetworkManagerImpl.java @@ -0,0 +1,83 @@ +package me.shedaniel.architectury.networking.fabric; + +import me.shedaniel.architectury.networking.NetworkManager; +import me.shedaniel.architectury.networking.NetworkManager.NetworkReceiver; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.network.ClientSidePacketRegistry; +import net.fabricmc.fabric.api.network.PacketContext; +import net.fabricmc.fabric.api.network.ServerSidePacketRegistry; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; + +public class NetworkManagerImpl implements NetworkManager.Impl { + @Override + public void registerReceiver(NetworkManager.Side side, ResourceLocation id, NetworkReceiver receiver) { + if (side == NetworkManager.Side.C2S) { + registerC2SReceiver(id, receiver); + } else if (side == NetworkManager.Side.S2C) { + registerS2CReceiver(id, receiver); + } + } + + private void registerC2SReceiver(ResourceLocation id, NetworkReceiver receiver) { + ServerSidePacketRegistry.INSTANCE.register(id, (packetContext, buf) -> receiver.receive(buf, to(packetContext))); + } + + @Environment(EnvType.CLIENT) + private void registerS2CReceiver(ResourceLocation id, NetworkReceiver receiver) { + ClientSidePacketRegistry.INSTANCE.register(id, (packetContext, buf) -> receiver.receive(buf, to(packetContext))); + } + + private NetworkManager.PacketContext to(PacketContext context) { + return new NetworkManager.PacketContext() { + @Override + public Player getPlayer() { + return context.getPlayer(); + } + + @Override + public void queue(Runnable runnable) { + context.getTaskQueue().execute(runnable); + } + + @Override + public EnvType getEnv() { + return context.getPacketEnvironment(); + } + }; + } + + @Override + public Packet toPacket(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf) { + if (side == NetworkManager.Side.C2S) { + return toC2SPacket(id, buf); + } else if (side == NetworkManager.Side.S2C) { + return toS2CPacket(id, buf); + } + + throw new IllegalArgumentException("Invalid side: " + side); + } + + @Override + public boolean canServerReceive(ResourceLocation id) { + return ClientSidePacketRegistry.INSTANCE.canServerReceive(id); + } + + @Override + public boolean canPlayerReceive(ServerPlayer player, ResourceLocation id) { + return ServerSidePacketRegistry.INSTANCE.canPlayerReceive(player, id); + } + + @Environment(EnvType.CLIENT) + private Packet toC2SPacket(ResourceLocation id, FriendlyByteBuf buf) { + return ClientSidePacketRegistry.INSTANCE.toPacket(id, buf); + } + + private Packet toS2CPacket(ResourceLocation id, FriendlyByteBuf buf) { + return ServerSidePacketRegistry.INSTANCE.toPacket(id, buf); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/platform/fabric/PlatformImpl.java b/fabric/src/main/java/me/shedaniel/architectury/platform/fabric/PlatformImpl.java new file mode 100644 index 00000000..daf612a3 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/platform/fabric/PlatformImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.platform.fabric; + +import me.shedaniel.architectury.platform.Mod; +import me.shedaniel.architectury.platform.Platform; +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.metadata.ModMetadata; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class PlatformImpl implements Platform.Impl { + public static final Map CONFIG_SCREENS = new HashMap<>(); + private final Map mods = new HashMap<>(); + + @Override + public Path getGameFolder() { + return FabricLoader.getInstance().getGameDir(); + } + + @Override + public Path getConfigFolder() { + return FabricLoader.getInstance().getConfigDir(); + } + + @Override + public Path getModsFolder() { + return getGameFolder().resolve("mods"); + } + + @Override + public EnvType getEnv() { + return FabricLoader.getInstance().getEnvironmentType(); + } + + @Override + public boolean isModLoaded(String id) { + return FabricLoader.getInstance().isModLoaded(id); + } + + @Override + public Mod getMod(String id) { + return this.mods.computeIfAbsent(id, ModImpl::new); + } + + @Override + public Collection getMods() { + for (ModContainer mod : FabricLoader.getInstance().getAllMods()) { + getMod(mod.getMetadata().getId()); + } + return this.mods.values(); + } + + private static class ModImpl implements Mod { + private final ModMetadata metadata; + + public ModImpl(String id) { + this.metadata = FabricLoader.getInstance().getModContainer(id).get().getMetadata(); + } + + @Override + public @NotNull String getModId() { + return metadata.getId(); + } + + @Override + public @NotNull String getVersion() { + return metadata.getVersion().getFriendlyString(); + } + + @Override + public @NotNull String getName() { + return metadata.getName(); + } + + @Override + public @NotNull String getDescription() { + return metadata.getDescription(); + } + + @Override + public void registerConfigurationScreen(ConfigurationScreenProvider provider) { + if (CONFIG_SCREENS.containsKey(getModId())) + throw new IllegalStateException("Can not register configuration screen for mod '" + getModId() + "' because it was already registered!"); + CONFIG_SCREENS.put(getModId(), provider); + } + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java new file mode 100644 index 00000000..f48f0134 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.registry.fabric; + +import me.shedaniel.architectury.registry.Registries; +import me.shedaniel.architectury.registry.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.LazyLoadedValue; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +public class RegistriesImpl implements Registries.Impl { + @Override + public Registries.RegistryProvider get(String modId) { + return RegistryProviderImpl.INSTANCE; + } + + public enum RegistryProviderImpl implements Registries.RegistryProvider { + INSTANCE; + + @Override + public Registry get(ResourceKey> key) { + return new RegistryImpl<>((net.minecraft.core.Registry) net.minecraft.core.Registry.REGISTRY.get(key.location())); + } + + @Override + public Registry get(net.minecraft.core.Registry registry) { + return new RegistryImpl<>(registry); + } + } + + public static class RegistryImpl implements Registry { + private net.minecraft.core.Registry delegate; + + public RegistryImpl(net.minecraft.core.Registry delegate) { + this.delegate = delegate; + } + + @Override + public Supplier delegate(ResourceLocation id) { + LazyLoadedValue value = new LazyLoadedValue<>(() -> get(id)); + return value::get; + } + + @Override + public Supplier register(ResourceLocation id, Supplier supplier) { + net.minecraft.core.Registry.register(delegate, id, supplier.get()); + return delegate(id); + } + + @Override + public @Nullable ResourceLocation getId(T obj) { + return delegate.getKey(obj); + } + + @Override + public @Nullable T get(ResourceLocation id) { + return delegate.get(id); + } + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/utils/fabric/GameInstanceImpl.java b/fabric/src/main/java/me/shedaniel/architectury/utils/fabric/GameInstanceImpl.java new file mode 100644 index 00000000..2d45152f --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/utils/fabric/GameInstanceImpl.java @@ -0,0 +1,33 @@ +package me.shedaniel.architectury.utils.fabric; + +import me.shedaniel.architectury.event.events.LifecycleEvent; +import me.shedaniel.architectury.platform.Platform; +import me.shedaniel.architectury.utils.GameInstance; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.server.MinecraftServer; + +public class GameInstanceImpl implements GameInstance.Impl { + private static MinecraftServer server = null; + + @Override + public MinecraftServer getServer() { + MinecraftServer server = null; + if (GameInstanceImpl.server != null) server = GameInstanceImpl.server; + if (Platform.getEnv() == EnvType.CLIENT) { + server = getServerFromClient(); + } + return server; + } + + public static void init() { + LifecycleEvent.SERVER_STARTING.register(server -> GameInstanceImpl.server = server); + LifecycleEvent.SERVER_STOPPED.register(server -> GameInstanceImpl.server = null); + } + + @Environment(EnvType.CLIENT) + private static MinecraftServer getServerFromClient() { + return Minecraft.getInstance().getSingleplayerServer(); + } +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..0848789b --- /dev/null +++ b/fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "architectury", + "version": "${version}", + "name": "Architectury", + "description": "This is an example description! Tell everyone what your mod is about!", + "authors": [ + "shedaniel" + ], + "license": "MIT", + "environment": "*", + "mixins": [ + "architectury.mixins.json" + ], + "entrypoints": { + "main": [ + "me.shedaniel.architectury.utils.fabric.GameInstanceImpl::init" + ], + "modmenu": [ + "me.shedaniel.architectury.compat.fabric.ModMenuCompatibility" + ] + }, + "depends": { + "minecraft": ">=1.16.2" + } +} \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle new file mode 100644 index 00000000..a6d49c8d --- /dev/null +++ b/forge/build.gradle @@ -0,0 +1,71 @@ +buildscript { + repositories { + maven { url "https://files.minecraftforge.net/maven" } + jcenter() + mavenCentral() + } + dependencies { + classpath(group: "net.minecraftforge.gradle", name: "ForgeGradle", version: "3.+", changing: true) + } +} + +plugins { + id "com.github.johnrengelman.shadow" version "5.0.0" + id "eclipse" +} + +apply plugin: "net.minecraftforge.gradle" + +minecraft { + mappings(channel: "official", version: rootProject.architect.minecraft) + accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') + runs { + client { + workingDirectory project.file("run") + mods { + examplemod { + source sourceSets.main + } + } + } + server { + workingDirectory project.file("run") + mods { + examplemod { + source sourceSets.main + } + } + } + } +} + +repositories { + jcenter() + maven { url "https://files.minecraftforge.net/maven" } +} + +configurations { + shadow +} + +dependencies { + minecraft("net.minecraftforge:forge:${rootProject.architect.minecraft}-${rootProject.forge_version}") + + compile(project(path: ":common", configuration: "mcpGenerateMod")) { + transitive = false + } + shadow(project(path: ":common", configuration: "mcp")) { + transitive = false + } +} + +shadowJar { + exclude "fabric.mod.json" + + configurations = [project.configurations.shadow] + classifier null +} + +reobf { + shadowJar {} +} \ No newline at end of file diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java new file mode 100644 index 00000000..a189f739 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java @@ -0,0 +1,124 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.forge; + +import me.shedaniel.architectury.event.EventFactory; +import me.shedaniel.architectury.event.events.GuiEvent; +import me.shedaniel.architectury.event.events.LifecycleEvent; +import me.shedaniel.architectury.event.events.TickEvent; +import me.shedaniel.architectury.event.events.TooltipEvent; +import net.minecraft.client.Minecraft; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.TickEvent.ClientTickEvent; +import net.minecraftforge.event.TickEvent.Phase; +import net.minecraftforge.event.TickEvent.ServerTickEvent; +import net.minecraftforge.event.TickEvent.WorldTickEvent; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.fml.event.server.FMLServerStartedEvent; +import net.minecraftforge.fml.event.server.FMLServerStartingEvent; +import net.minecraftforge.fml.event.server.FMLServerStoppedEvent; +import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; +import net.minecraftforge.fml.server.ServerLifecycleHooks; + +public class EventFactoryImpl implements EventFactory.Impl { + @Override + public void registerClient() { + MinecraftForge.EVENT_BUS.register(Client.class); + } + + @Override + public void registerCommon() { + MinecraftForge.EVENT_BUS.register(Common.class); + } + + @Override + public void registerServer() { + MinecraftForge.EVENT_BUS.register(Server.class); + } + + @OnlyIn(Dist.CLIENT) + public static class Client { + @SubscribeEvent + public static void event(ItemTooltipEvent event) { + TooltipEvent.ITEM.invoker().append(event.getItemStack(), event.getToolTip(), event.getFlags()); + } + + @SubscribeEvent + public static void event(ClientTickEvent event) { + if (event.phase == Phase.START) + TickEvent.CLIENT_PRE.invoker().tick(Minecraft.getInstance()); + else if (event.phase == Phase.END) + TickEvent.CLIENT_POST.invoker().tick(Minecraft.getInstance()); + } + + @SubscribeEvent + public static void event(RenderGameOverlayEvent.Post event) { + GuiEvent.RENDER_HUD.invoker().renderHud(event.getMatrixStack(), event.getPartialTicks()); + } + } + + public static class Common { + @SubscribeEvent + public static void event(ServerTickEvent event) { + if (event.phase == Phase.START) + TickEvent.SERVER_PRE.invoker().tick(ServerLifecycleHooks.getCurrentServer()); + else if (event.phase == Phase.END) + TickEvent.SERVER_POST.invoker().tick(ServerLifecycleHooks.getCurrentServer()); + } + + @SubscribeEvent + public static void event(WorldTickEvent event) { + if (event.side == LogicalSide.SERVER) { + if (event.phase == Phase.START) + TickEvent.SERVER_WORLD_PRE.invoker().tick((ServerWorld) event.world); + else if (event.phase == Phase.END) + TickEvent.SERVER_WORLD_POST.invoker().tick((ServerWorld) event.world); + } + } + + @SubscribeEvent + public static void event(FMLServerStartingEvent event) { + LifecycleEvent.SERVER_STARTING.invoker().stateChanged(event.getServer()); + } + + @SubscribeEvent + public static void event(FMLServerStartedEvent event) { + LifecycleEvent.SERVER_STARTED.invoker().stateChanged(event.getServer()); + } + + @SubscribeEvent + public static void event(FMLServerStoppingEvent event) { + LifecycleEvent.SERVER_STOPPING.invoker().stateChanged(event.getServer()); + } + + @SubscribeEvent + public static void event(FMLServerStoppedEvent event) { + LifecycleEvent.SERVER_STOPPED.invoker().stateChanged(event.getServer()); + } + } + + @OnlyIn(Dist.DEDICATED_SERVER) + public static class Server { + + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/networking/forge/ClientNetworkingManager.java b/forge/src/main/java/me/shedaniel/architectury/networking/forge/ClientNetworkingManager.java new file mode 100644 index 00000000..51b29b69 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/networking/forge/ClientNetworkingManager.java @@ -0,0 +1,39 @@ +package me.shedaniel.architectury.networking.forge; + +import me.shedaniel.architectury.networking.NetworkManager; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.event.ClientPlayerNetworkEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.Set; +import java.util.function.Consumer; + +import static me.shedaniel.architectury.networking.forge.NetworkManagerImpl.C2S; +import static me.shedaniel.architectury.networking.forge.NetworkManagerImpl.SYNC_IDS; + +@OnlyIn(Dist.CLIENT) +public class ClientNetworkingManager { + public static Consumer initClient() { + NetworkManagerImpl.CHANNEL.addListener(NetworkManagerImpl.createPacketHandler(NetworkEvent.ServerCustomPayloadEvent.class, NetworkManagerImpl.S2C)); + MinecraftForge.EVENT_BUS.addListener(event -> NetworkManagerImpl.serverReceivables.clear()); + + return impl -> impl.registerS2CReceiver(SYNC_IDS, (buffer, context) -> { + Set receivables = NetworkManagerImpl.serverReceivables; + int size = buffer.readInt(); + receivables.clear(); + for (int i = 0; i < size; i++) { + receivables.add(buffer.readResourceLocation()); + } + NetworkManager.sendToServer(SYNC_IDS, NetworkManagerImpl.sendSyncPacket(C2S)); + }); + } + + public static PlayerEntity getClientPlayer() { + return Minecraft.getInstance().player; + } +} \ No newline at end of file diff --git a/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java b/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java new file mode 100644 index 00000000..d65ece89 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java @@ -0,0 +1,137 @@ +package me.shedaniel.architectury.networking.forge; + + +import com.google.common.collect.*; +import io.netty.buffer.Unpooled; +import me.shedaniel.architectury.networking.NetworkManager; +import me.shedaniel.architectury.networking.NetworkManager.NetworkReceiver; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.network.IPacket; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.fml.network.NetworkDirection; +import net.minecraftforge.fml.network.NetworkEvent; +import net.minecraftforge.fml.network.NetworkRegistry; +import net.minecraftforge.fml.network.event.EventNetworkChannel; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +public class NetworkManagerImpl implements NetworkManager.Impl { + @Override + public void registerReceiver(NetworkManager.Side side, ResourceLocation id, NetworkReceiver receiver) { + if (side == NetworkManager.Side.C2S) { + registerC2SReceiver(id, receiver); + } else if (side == NetworkManager.Side.S2C) { + registerS2CReceiver(id, receiver); + } + } + + @Override + public IPacket toPacket(NetworkManager.Side side, ResourceLocation id, PacketBuffer buffer) { + PacketBuffer packetBuffer = new PacketBuffer(Unpooled.buffer()); + packetBuffer.writeResourceLocation(id); + packetBuffer.writeBytes(buffer); + return (side == NetworkManager.Side.C2S ? NetworkDirection.PLAY_TO_SERVER : NetworkDirection.PLAY_TO_CLIENT).buildPacket(Pair.of(packetBuffer, 0), CHANNEL_ID).getThis(); + } + + private static final ResourceLocation CHANNEL_ID = new ResourceLocation("architectury:network"); + static final ResourceLocation SYNC_IDS = new ResourceLocation("architectury:sync_ids"); + static final EventNetworkChannel CHANNEL = NetworkRegistry.newEventChannel(CHANNEL_ID, () -> "1", version -> true, version -> true); + static final Map S2C = Maps.newHashMap(); + static final Map C2S = Maps.newHashMap(); + static final Set serverReceivables = Sets.newHashSet(); + private static final Multimap clientReceivables = Multimaps.newMultimap(Maps.newHashMap(), Sets::newHashSet); + + public NetworkManagerImpl() { + CHANNEL.addListener(createPacketHandler(NetworkEvent.ClientCustomPayloadEvent.class, C2S)); + + DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> ClientNetworkingManager::initClient).accept(this); + + MinecraftForge.EVENT_BUS.addListener(event -> NetworkManager.sendToPlayer((ServerPlayerEntity) event.getPlayer(), SYNC_IDS, sendSyncPacket(C2S))); + MinecraftForge.EVENT_BUS.addListener(event -> clientReceivables.removeAll(event.getPlayer())); + + registerC2SReceiver(SYNC_IDS, (buffer, context) -> { + Set receivables = (Set) clientReceivables.get(context.getPlayer()); + int size = buffer.readInt(); + receivables.clear(); + for (int i = 0; i < size; i++) { + receivables.add(buffer.readResourceLocation()); + } + }); + } + + static Consumer createPacketHandler(Class clazz, Map map) { + return event -> { + if (event.getClass() != clazz) return; + NetworkEvent.Context context = event.getSource().get(); + if (context.getPacketHandled()) return; + PacketBuffer buffer = new PacketBuffer(event.getPayload().copy()); + ResourceLocation type = buffer.readResourceLocation(); + NetworkReceiver receiver = map.get(type); + + if (receiver != null) { + receiver.receive(buffer, new NetworkManager.PacketContext() { + @Override + public PlayerEntity getPlayer() { + return getEnv() == Dist.CLIENT ? getClientPlayer() : context.getSender(); + } + + @Override + public void queue(Runnable runnable) { + context.enqueueWork(runnable); + } + + @Override + public Dist getEnv() { + return context.getDirection().getReceptionSide() == LogicalSide.CLIENT ? Dist.CLIENT : Dist.DEDICATED_SERVER; + } + + private PlayerEntity getClientPlayer() { + return DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> ClientNetworkingManager::getClientPlayer); + } + }); + } + context.setPacketHandled(true); + }; + } + + @OnlyIn(Dist.CLIENT) + public void registerS2CReceiver(ResourceLocation id, NetworkReceiver receiver) { + S2C.put(id, receiver); + } + + public void registerC2SReceiver(ResourceLocation id, NetworkReceiver receiver) { + C2S.put(id, receiver); + } + + @Override + public boolean canServerReceive(ResourceLocation id) { + return serverReceivables.contains(id); + } + + @Override + public boolean canPlayerReceive(ServerPlayerEntity player, ResourceLocation id) { + return clientReceivables.get(player).contains(id); + } + + static PacketBuffer sendSyncPacket(Map map) { + List availableIds = Lists.newArrayList(map.keySet()); + PacketBuffer packetBuffer = new PacketBuffer(Unpooled.buffer()); + packetBuffer.writeInt(availableIds.size()); + for (ResourceLocation availableId : availableIds) { + packetBuffer.writeResourceLocation(availableId); + } + return packetBuffer; + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/platform/forge/EventBuses.java b/forge/src/main/java/me/shedaniel/architectury/platform/forge/EventBuses.java new file mode 100644 index 00000000..e36fb46d --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/platform/forge/EventBuses.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.platform.forge; + +import net.minecraftforge.eventbus.api.IEventBus; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public final class EventBuses { + private EventBuses() {} + + private static final Map EVENT_BUS_MAP = new HashMap<>(); + + public static void registerModEventBus(String modId, IEventBus bus) { + IEventBus previous = EVENT_BUS_MAP.put(modId, bus); + if (previous != null) { + EVENT_BUS_MAP.put(modId, previous); + throw new IllegalStateException("Can't register event bus for mod '" + modId + "' because it was previously registered!"); + } + } + + public static Optional getModEventBus(String modId) { + return Optional.ofNullable(EVENT_BUS_MAP.get(modId)); + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/platform/forge/PlatformImpl.java b/forge/src/main/java/me/shedaniel/architectury/platform/forge/PlatformImpl.java new file mode 100644 index 00000000..c0aee049 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/platform/forge/PlatformImpl.java @@ -0,0 +1,114 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.platform.forge; + +import me.shedaniel.architectury.platform.Mod; +import me.shedaniel.architectury.platform.Platform; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.ExtensionPoint; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.FMLEnvironment; +import net.minecraftforge.fml.loading.FMLPaths; +import net.minecraftforge.forgespi.language.IModInfo; + +import javax.annotation.Nonnull; +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class PlatformImpl implements Platform.Impl { + private final Map mods = new HashMap<>(); + + @Override + public Path getGameFolder() { + return FMLPaths.GAMEDIR.get(); + } + + @Override + public Path getConfigFolder() { + return FMLPaths.CONFIGDIR.get(); + } + + @Override + public Path getModsFolder() { + return FMLPaths.MODSDIR.get(); + } + + @Override + public Dist getEnv() { + return FMLEnvironment.dist; + } + + @Override + public boolean isModLoaded(String id) { + return ModList.get().isLoaded(id); + } + + @Override + public Mod getMod(String id) { + return this.mods.computeIfAbsent(id, ModImpl::new); + } + + @Override + public Collection getMods() { + for (IModInfo mod : ModList.get().getMods()) { + getMod(mod.getModId()); + } + return this.mods.values(); + } + + private static class ModImpl implements Mod { + private final ModContainer container; + private final IModInfo metadata; + + public ModImpl(String id) { + this.container = ModList.get().getModContainerById(id).get(); + this.metadata = container.getModInfo(); + } + + @Override + @Nonnull + public String getModId() { + return metadata.getModId(); + } + + @Override + @Nonnull + public String getVersion() { + return metadata.getVersion().toString(); + } + + @Override + @Nonnull + public String getName() { + return metadata.getDisplayName(); + } + + @Override + @Nonnull + public String getDescription() { + return metadata.getDescription(); + } + + @Override + public void registerConfigurationScreen(ConfigurationScreenProvider configurationScreenProvider) { + container.registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY, () -> (minecraft, screen) -> configurationScreenProvider.provide(screen)); + } + } +} \ No newline at end of file diff --git a/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java new file mode 100644 index 00000000..8abf30b7 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java @@ -0,0 +1,150 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.registry.forge; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import me.shedaniel.architectury.platform.forge.EventBuses; +import me.shedaniel.architectury.registry.Registries; +import me.shedaniel.architectury.registry.Registry; +import net.minecraft.util.LazyValue; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.RegistryObject; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; +import net.minecraftforge.registries.RegistryManager; + +import javax.annotation.Nullable; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.function.Supplier; + +public class RegistriesImpl implements Registries.Impl { + @Override + public Registries.RegistryProvider get(String modId) { + return new RegistryProviderImpl(modId); + } + + public static class RegistryProviderImpl implements Registries.RegistryProvider { + private final String modId; + private final IEventBus eventBus; + private final Table, Supplier>> registry = HashBasedTable.create(); + + public RegistryProviderImpl(String modId) { + this.modId = modId; + this.eventBus = EventBuses.getModEventBus(modId).orElseThrow(() -> new IllegalStateException("Can't get event bus for mod '" + modId + "' because it was not registered!")); + this.eventBus.register(new EventListener()); + } + + @Override + public Registry get(RegistryKey> registryKey) { + return new ForgeBackedRegistryImpl<>(registry, (IForgeRegistry) RegistryManager.ACTIVE.getRegistry(registryKey.location())); + } + + @Override + public Registry get(net.minecraft.util.registry.Registry registry) { + return new VanillaBackedRegistryImpl<>(registry); + } + + public class EventListener { + @SubscribeEvent + public void handleEvent(RegistryEvent.Register event) { + IForgeRegistry registry = event.getRegistry(); + + for (Map.Entry, Supplier>>> row : RegistryProviderImpl.this.registry.rowMap().entrySet()) { + if (row.getKey() == event.getGenericType()) { + for (Map.Entry, Supplier>> entry : row.getValue().entrySet()) { + registry.register(entry.getValue().get()); + entry.getKey().updateReference(registry); + } + } + } + } + } + } + + public static class VanillaBackedRegistryImpl implements Registry { + private net.minecraft.util.registry.Registry delegate; + + public VanillaBackedRegistryImpl(net.minecraft.util.registry.Registry delegate) { + this.delegate = delegate; + } + + @Override + public Supplier delegate(ResourceLocation id) { + LazyValue value = new LazyValue<>(() -> get(id)); + return value::get; + } + + @Override + public Supplier register(ResourceLocation id, Supplier supplier) { + net.minecraft.util.registry.Registry.register(delegate, id, supplier.get()); + return delegate(id); + } + + @Override + @Nullable + public ResourceLocation getId(T obj) { + return delegate.getKey(obj); + } + + @Override + @Nullable + public T get(ResourceLocation id) { + return delegate.get(id); + } + } + + public static class ForgeBackedRegistryImpl> implements Registry { + private IForgeRegistry delegate; + private Table, Supplier>> registry; + + public ForgeBackedRegistryImpl(Table, Supplier>> registry, IForgeRegistry delegate) { + this.registry = registry; + this.delegate = delegate; + } + + @Override + public Supplier delegate(ResourceLocation id) { + LazyValue value = new LazyValue<>(() -> get(id)); + return value::get; + } + + @Override + public Supplier register(ResourceLocation id, Supplier supplier) { + RegistryObject registryObject = RegistryObject.of(id, delegate); + registry.put(delegate.getRegistrySuperType(), registryObject, () -> supplier.get().setRegistryName(id)); + return registryObject; + } + + @Override + @Nullable + public ResourceLocation getId(T obj) { + return delegate.getKey(obj); + } + + @Override + @Nullable + public T get(ResourceLocation id) { + return delegate.getValue(id); + } + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/utils/forge/GameInstanceImpl.java b/forge/src/main/java/me/shedaniel/architectury/utils/forge/GameInstanceImpl.java new file mode 100644 index 00000000..e4654a70 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/utils/forge/GameInstanceImpl.java @@ -0,0 +1,12 @@ +package me.shedaniel.architectury.utils.forge; + +import me.shedaniel.architectury.utils.GameInstance; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fml.server.ServerLifecycleHooks; + +public class GameInstanceImpl implements GameInstance.Impl { + @Override + public MinecraftServer getServer() { + return ServerLifecycleHooks.getCurrentServer(); + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..2545acc2 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,11 @@ +minecraft_version=1.16.3 + +archives_base_name=architectury +mod_version=1.0.0 +maven_group=me.shedaniel + +fabric_loader_version=0.10.5+build.213 +fabric_api_version=0.24.3+build.414-1.16 +mod_menu_version=1.14.6+ + +forge_version=34.1.34 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..4b7e1f3d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/logs/latest.log b/logs/latest.log new file mode 100644 index 00000000..e69de29b diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..4e80b2d2 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,14 @@ +pluginManagement { + repositories { + jcenter() + maven { url "https://maven.fabricmc.net/" } + maven { url "https://dl.bintray.com/shedaniel/cloth" } + gradlePluginPortal() + } +} + +include("common") +include("fabric") +include("forge") + +rootProject.name = "architectury"