Browse Source

Initial work

shedaniel 4 years ago
commit
5a39747a65
44 changed files with 2562 additions and 0 deletions
  1. 16 0
      .gitignore
  2. 13 0
      HEADER
  3. 170 0
      LICENSE.md
  4. 36 0
      build.gradle
  5. 16 0
      common/build.gradle
  6. 39 0
      common/src/main/java/me/shedaniel/architectury/Architectury.java
  7. 57 0
      common/src/main/java/me/shedaniel/architectury/ArchitecturyPopulator.java
  8. 27 0
      common/src/main/java/me/shedaniel/architectury/Populatable.java
  9. 29 0
      common/src/main/java/me/shedaniel/architectury/event/Event.java
  10. 150 0
      common/src/main/java/me/shedaniel/architectury/event/EventFactory.java
  11. 29 0
      common/src/main/java/me/shedaniel/architectury/event/events/GuiEvent.java
  12. 44 0
      common/src/main/java/me/shedaniel/architectury/event/events/LifecycleEvent.java
  13. 56 0
      common/src/main/java/me/shedaniel/architectury/event/events/TickEvent.java
  14. 37 0
      common/src/main/java/me/shedaniel/architectury/event/events/TooltipEvent.java
  15. 115 0
      common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java
  16. 45 0
      common/src/main/java/me/shedaniel/architectury/platform/Mod.java
  17. 90 0
      common/src/main/java/me/shedaniel/architectury/platform/Platform.java
  18. 63 0
      common/src/main/java/me/shedaniel/architectury/registry/Registries.java
  19. 34 0
      common/src/main/java/me/shedaniel/architectury/registry/Registry.java
  20. 31 0
      common/src/main/java/me/shedaniel/architectury/utils/GameInstance.java
  21. 6 0
      common/src/main/resources/fabric.mod.json
  22. 33 0
      fabric/build.gradle
  23. 43 0
      fabric/src/main/java/me/shedaniel/architectury/compat/fabric/ModMenuCompatibility.java
  24. 63 0
      fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java
  25. 83 0
      fabric/src/main/java/me/shedaniel/architectury/networking/fabric/NetworkManagerImpl.java
  26. 108 0
      fabric/src/main/java/me/shedaniel/architectury/platform/fabric/PlatformImpl.java
  27. 77 0
      fabric/src/main/java/me/shedaniel/architectury/registry/fabric/RegistriesImpl.java
  28. 33 0
      fabric/src/main/java/me/shedaniel/architectury/utils/fabric/GameInstanceImpl.java
  29. 26 0
      fabric/src/main/resources/fabric.mod.json
  30. 71 0
      forge/build.gradle
  31. 124 0
      forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java
  32. 39 0
      forge/src/main/java/me/shedaniel/architectury/networking/forge/ClientNetworkingManager.java
  33. 137 0
      forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java
  34. 42 0
      forge/src/main/java/me/shedaniel/architectury/platform/forge/EventBuses.java
  35. 114 0
      forge/src/main/java/me/shedaniel/architectury/platform/forge/PlatformImpl.java
  36. 150 0
      forge/src/main/java/me/shedaniel/architectury/registry/forge/RegistriesImpl.java
  37. 12 0
      forge/src/main/java/me/shedaniel/architectury/utils/forge/GameInstanceImpl.java
  38. 11 0
      gradle.properties
  39. BIN
      gradle/wrapper/gradle-wrapper.jar
  40. 5 0
      gradle/wrapper/gradle-wrapper.properties
  41. 185 0
      gradlew
  42. 89 0
      gradlew.bat
  43. 0 0
      logs/latest.log
  44. 14 0
      settings.gradle

+ 16 - 0
.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/

+ 13 - 0
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.

+ 170 - 0
LICENSE.md

@@ -0,0 +1,170 @@
+Apache License
+==============
+
+_Version 2.0, January 2004_  
+_&lt;<http://www.apache.org/licenses/>&gt;_
+
+### 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_

+ 36 - 0
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") }

+ 16 - 0
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()
+}

+ 39 - 0
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;
+    }
+}

+ 57 - 0
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);
+        }
+    }
+}

+ 27 - 0
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 {
+}

+ 29 - 0
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> {
+    T invoker();
+    
+    void register(T listener);
+    
+    void unregister(T listener);
+    
+    boolean isRegistered(T listener);
+    
+    void clearListeners();
+}

+ 150 - 0
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 <T> Event<T> create(Function<T[], T> function) {
+        Class<?>[] arguments = TypeResolver.resolveRawArguments(Function.class, function.getClass());
+        return new EventImpl<>(arguments[1], function);
+    }
+    
+    @SuppressWarnings("UnstableApiUsage")
+    public static <T> Event<T> createLoop(Class<T> 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 <T> Event<T> createInteractionResult(Class<T> 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<T> implements Event<T> {
+        private final Function<T[], T> function;
+        private T invoker = null;
+        private T[] listeners;
+        private Class<?> clazz;
+        
+        public EventImpl(Class<?> clazz, Function<T[], T> 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();
+    }
+}

+ 29 - 0
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<RenderHud> RENDER_HUD = EventFactory.createLoop(RenderHud.class);
+    
+    interface RenderHud {
+        void renderHud(PoseStack matrices, float tickDelta);
+    }
+}

+ 44 - 0
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<ClientState> CLIENT_STARTED = EventFactory.createLoop(ClientState.class);
+    @Environment(EnvType.CLIENT)
+    Event<ClientState> CLIENT_STOPPING = EventFactory.createLoop(ClientState.class);
+    Event<ServerState> SERVER_STARTING = EventFactory.createLoop(ServerState.class);
+    Event<ServerState> SERVER_STARTED = EventFactory.createLoop(ServerState.class);
+    Event<ServerState> SERVER_STOPPING = EventFactory.createLoop(ServerState.class);
+    Event<ServerState> SERVER_STOPPED = EventFactory.createLoop(ServerState.class);
+    
+    interface InstanceState<T> {
+        void stateChanged(T instance);
+    }
+    
+    @Environment(EnvType.CLIENT)
+    interface ClientState extends InstanceState<Minecraft> {}
+    
+    interface ServerState extends InstanceState<MinecraftServer> {}
+}

+ 56 - 0
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<T> {
+    @Environment(EnvType.CLIENT)
+    Event<Client> CLIENT_PRE = EventFactory.createLoop(Client.class);
+    @Environment(EnvType.CLIENT)
+    Event<Client> CLIENT_POST = EventFactory.createLoop(Client.class);
+    Event<Server> SERVER_PRE = EventFactory.createLoop(Server.class);
+    Event<Server> SERVER_POST = EventFactory.createLoop(Server.class);
+    @Environment(EnvType.CLIENT)
+    Event<ClientWorld> CLIENT_WORLD_PRE = EventFactory.createLoop(ClientWorld.class);
+    @Environment(EnvType.CLIENT)
+    Event<ClientWorld> CLIENT_WORLD_POST = EventFactory.createLoop(ClientWorld.class);
+    Event<ServerWorld> SERVER_WORLD_PRE = EventFactory.createLoop(ServerWorld.class);
+    Event<ServerWorld> SERVER_WORLD_POST = EventFactory.createLoop(ServerWorld.class);
+    
+    void tick(T instance);
+    
+    @Environment(EnvType.CLIENT)
+    interface Client extends TickEvent<Minecraft> {}
+    
+    interface Server extends TickEvent<MinecraftServer> {}
+    
+    interface WorldTick<T extends Level> extends TickEvent<T> {}
+    
+    @Environment(EnvType.CLIENT)
+    interface ClientWorld extends WorldTick<ClientLevel> {}
+    
+    interface ServerWorld extends WorldTick<ServerLevel> {}
+}

+ 37 - 0
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> ITEM = EventFactory.createLoop(Item.class);
+    
+    @Environment(EnvType.CLIENT)
+    interface Item {
+        void append(ItemStack stack, List<Component> lines, TooltipFlag flag);
+    }
+}

+ 115 - 0
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<ServerPlayer> 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);
+    }
+}

+ 45 - 0
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);
+    }
+}

+ 90 - 0
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<Mod> getMods() {
+        return IMPL.getMods();
+    }
+    
+    public interface Impl {
+        Path getGameFolder();
+        
+        Path getConfigFolder();
+        
+        Path getModsFolder();
+        
+        EnvType getEnv();
+        
+        boolean isModLoaded(String id);
+        
+        Mod getMod(String id);
+        
+        Collection<Mod> getMods();
+    }
+    
+    static {
+        ArchitecturyPopulator.populate(Platform.class);
+    }
+}

+ 63 - 0
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<String, Registries> 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 <T> Registry<T> get(ResourceKey<net.minecraft.core.Registry<T>> key) {
+        return this.provider.get(key);
+    }
+    
+    @Deprecated
+    public <T> Registry<T> get(net.minecraft.core.Registry<T> registry) {
+        return this.provider.get(registry);
+    }
+    
+    public interface Impl {
+        RegistryProvider get(String modId);
+    }
+    
+    public interface RegistryProvider {
+        <T> Registry<T> get(ResourceKey<net.minecraft.core.Registry<T>> key);
+        
+        @Deprecated
+        <T> Registry<T> get(net.minecraft.core.Registry<T> registry);
+    }
+    
+    static {
+        ArchitecturyPopulator.populate(Registries.class);
+    }
+}

+ 34 - 0
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<T> {
+    Supplier<T> delegate(ResourceLocation id);
+    
+    Supplier<T> register(ResourceLocation id, Supplier<T> supplier);
+    
+    @Nullable
+    ResourceLocation getId(T obj);
+    
+    @Nullable
+    T get(ResourceLocation id);
+}

+ 31 - 0
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);
+    }
+}

+ 6 - 0
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"
+}

+ 33 - 0
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)
+}

+ 43 - 0
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<String, ConfigScreenFactory<?>> FACTORIES = Maps.newHashMap();
+    
+    @Override
+    public Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
+        validateMap();
+        return FACTORIES;
+    }
+    
+    private void validateMap() {
+        for (Map.Entry<String, Mod.ConfigurationScreenProvider> entry : PlatformImpl.CONFIG_SCREENS.entrySet()) {
+            if (!FACTORIES.containsKey(entry.getKey())) {
+                FACTORIES.put(entry.getKey(), entry.getValue()::provide);
+            }
+        }
+    }
+}

+ 63 - 0
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() {
+        
+    }
+}

+ 83 - 0
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);
+    }
+}

+ 108 - 0
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<String, Mod.ConfigurationScreenProvider> CONFIG_SCREENS = new HashMap<>();
+    private final Map<String, Mod> 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<Mod> 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);
+        }
+    }
+}

+ 77 - 0
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 <T> Registry<T> get(ResourceKey<net.minecraft.core.Registry<T>> key) {
+            return new RegistryImpl<>((net.minecraft.core.Registry<T>) net.minecraft.core.Registry.REGISTRY.get(key.location()));
+        }
+        
+        @Override
+        public <T> Registry<T> get(net.minecraft.core.Registry<T> registry) {
+            return new RegistryImpl<>(registry);
+        }
+    }
+    
+    public static class RegistryImpl<T> implements Registry<T> {
+        private net.minecraft.core.Registry<T> delegate;
+        
+        public RegistryImpl(net.minecraft.core.Registry<T> delegate) {
+            this.delegate = delegate;
+        }
+        
+        @Override
+        public Supplier<T> delegate(ResourceLocation id) {
+            LazyLoadedValue<T> value = new LazyLoadedValue<>(() -> get(id));
+            return value::get;
+        }
+        
+        @Override
+        public Supplier<T> register(ResourceLocation id, Supplier<T> 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);
+        }
+    }
+}

+ 33 - 0
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();
+    }
+}

+ 26 - 0
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"
+  }
+}

+ 71 - 0
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 {}
+}

+ 124 - 0
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 {
+        
+    }
+}

+ 39 - 0
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<NetworkManagerImpl> initClient() {
+        NetworkManagerImpl.CHANNEL.addListener(NetworkManagerImpl.createPacketHandler(NetworkEvent.ServerCustomPayloadEvent.class, NetworkManagerImpl.S2C));
+        MinecraftForge.EVENT_BUS.<ClientPlayerNetworkEvent.LoggedOutEvent>addListener(event -> NetworkManagerImpl.serverReceivables.clear());
+        
+        return impl -> impl.registerS2CReceiver(SYNC_IDS, (buffer, context) -> {
+            Set<ResourceLocation> 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;
+    }
+}

+ 137 - 0
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<ResourceLocation, NetworkReceiver> S2C = Maps.newHashMap();
+    static final Map<ResourceLocation, NetworkReceiver> C2S = Maps.newHashMap();
+    static final Set<ResourceLocation> serverReceivables = Sets.newHashSet();
+    private static final Multimap<PlayerEntity, ResourceLocation> 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.<PlayerEvent.PlayerLoggedInEvent>addListener(event -> NetworkManager.sendToPlayer((ServerPlayerEntity) event.getPlayer(), SYNC_IDS, sendSyncPacket(C2S)));
+        MinecraftForge.EVENT_BUS.<PlayerEvent.PlayerLoggedOutEvent>addListener(event -> clientReceivables.removeAll(event.getPlayer()));
+        
+        registerC2SReceiver(SYNC_IDS, (buffer, context) -> {
+            Set<ResourceLocation> receivables = (Set<ResourceLocation>) clientReceivables.get(context.getPlayer());
+            int size = buffer.readInt();
+            receivables.clear();
+            for (int i = 0; i < size; i++) {
+                receivables.add(buffer.readResourceLocation());
+            }
+        });
+    }
+    
+    static <T extends NetworkEvent> Consumer<T> createPacketHandler(Class<T> clazz, Map<ResourceLocation, NetworkReceiver> 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<ResourceLocation, NetworkReceiver> map) {
+        List<ResourceLocation> availableIds = Lists.newArrayList(map.keySet());
+        PacketBuffer packetBuffer = new PacketBuffer(Unpooled.buffer());
+        packetBuffer.writeInt(availableIds.size());
+        for (ResourceLocation availableId : availableIds) {
+            packetBuffer.writeResourceLocation(availableId);
+        }
+        return packetBuffer;
+    }
+}

+ 42 - 0
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<String, IEventBus> 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<IEventBus> getModEventBus(String modId) {
+        return Optional.ofNullable(EVENT_BUS_MAP.get(modId));
+    }
+}

+ 114 - 0
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<String, Mod> 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<Mod> 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));
+        }
+    }
+}

+ 150 - 0
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<Type, RegistryObject<?>, Supplier<? extends IForgeRegistryEntry<?>>> 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 <T> Registry<T> get(RegistryKey<net.minecraft.util.registry.Registry<T>> registryKey) {
+            return new ForgeBackedRegistryImpl<>(registry, (IForgeRegistry) RegistryManager.ACTIVE.getRegistry(registryKey.location()));
+        }
+        
+        @Override
+        public <T> Registry<T> get(net.minecraft.util.registry.Registry<T> registry) {
+            return new VanillaBackedRegistryImpl<>(registry);
+        }
+        
+        public class EventListener {
+            @SubscribeEvent
+            public void handleEvent(RegistryEvent.Register event) {
+                IForgeRegistry registry = event.getRegistry();
+                
+                for (Map.Entry<Type, Map<RegistryObject<?>, Supplier<? extends IForgeRegistryEntry<?>>>> row : RegistryProviderImpl.this.registry.rowMap().entrySet()) {
+                    if (row.getKey() == event.getGenericType()) {
+                        for (Map.Entry<RegistryObject<?>, Supplier<? extends IForgeRegistryEntry<?>>> entry : row.getValue().entrySet()) {
+                            registry.register(entry.getValue().get());
+                            entry.getKey().updateReference(registry);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    public static class VanillaBackedRegistryImpl<T> implements Registry<T> {
+        private net.minecraft.util.registry.Registry<T> delegate;
+        
+        public VanillaBackedRegistryImpl(net.minecraft.util.registry.Registry<T> delegate) {
+            this.delegate = delegate;
+        }
+        
+        @Override
+        public Supplier<T> delegate(ResourceLocation id) {
+            LazyValue<T> value = new LazyValue<>(() -> get(id));
+            return value::get;
+        }
+        
+        @Override
+        public Supplier<T> register(ResourceLocation id, Supplier<T> 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<T extends IForgeRegistryEntry<T>> implements Registry<T> {
+        private IForgeRegistry<T> delegate;
+        private Table<Type, RegistryObject<?>, Supplier<? extends IForgeRegistryEntry<?>>> registry;
+        
+        public ForgeBackedRegistryImpl(Table<Type, RegistryObject<?>, Supplier<? extends IForgeRegistryEntry<?>>> registry, IForgeRegistry<T> delegate) {
+            this.registry = registry;
+            this.delegate = delegate;
+        }
+        
+        @Override
+        public Supplier<T> delegate(ResourceLocation id) {
+            LazyValue<T> value = new LazyValue<>(() -> get(id));
+            return value::get;
+        }
+        
+        @Override
+        public Supplier<T> register(ResourceLocation id, Supplier<T> 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);
+        }
+    }
+}

+ 12 - 0
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();
+    }
+}

+ 11 - 0
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

BIN
gradle/wrapper/gradle-wrapper.jar


+ 5 - 0
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

+ 185 - 0
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" "$@"

+ 89 - 0
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

+ 0 - 0
logs/latest.log


+ 14 - 0
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"