Skip to content

Commit 7303781

Browse files
committed
Update 1.2.1
1 parent 867d0af commit 7303781

24 files changed

+814
-22
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ For mixins usage check here: https://github.com/2xsaiko/mixin-cheatsheet
1414

1515
For spark usage check here: https://spark.lucko.me/docs/Command-Usage
1616

17+
For example mod check here: https://github.com/Fox2Code/FoxLoaderExampleMod
18+
1719
## Project structure
1820
- `betacraft` -> BetaCraft launch method to run fox loader
1921
- `client` -> client specific code
@@ -38,3 +40,27 @@ As you see the game is allowed to be loaded very late into the boot process, eve
3840
This ensures that all patches introduced by mods are applied,
3941
but also prevent code loaded in MixinPlugins to load Minecraft classes,
4042
please keep that in mind when messing with mixins.
43+
44+
## Lua mods
45+
46+
If you are too lazy to learn java you can just use put `.lua` files in your `mods` folder
47+
48+
```lua
49+
-- modId: lua_example
50+
-- modName: Lua Example Mod
51+
-- version: 1.0.0
52+
-- description: Just add /discord to the game.
53+
--
54+
55+
print("Hello from lua")
56+
57+
gameRegistry.registerCommand(function(args, player)
58+
player:displayChatMessage(chatColors.AQUA .. "https://discord.gg/38Vfes6NpR")
59+
end, "discord", false) -- false is needed cause it default to "opOnly = true"
60+
61+
mod.onNetworkPlayerJoined = function(player)
62+
player:displayChatMessage(chatColors.AQUA .. "Welcome back to server name!")
63+
end
64+
```
65+
66+
Note: You can use the [`luajava`](https://github.com/luaj/luaj#user-content-luajava) extension from [LuaJ](https://github.com/luaj/luaj)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.fox2code.foxloader.client.mixins;
2+
3+
import com.fox2code.foxloader.registry.CommandCompat;
4+
import net.minecraft.mitask.utils.CommandHelperGUI;
5+
import org.spongepowered.asm.mixin.Mixin;
6+
import org.spongepowered.asm.mixin.Shadow;
7+
import org.spongepowered.asm.mixin.injection.At;
8+
import org.spongepowered.asm.mixin.injection.Inject;
9+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
10+
11+
import java.util.List;
12+
13+
@Mixin(CommandHelperGUI.class)
14+
public class MixinCommandHelperGUI {
15+
@Shadow static List<String> commandsNames;
16+
17+
@Inject(method = {"setSPCommands", "setMPCommands"}, at = @At("RETURN"))
18+
private static void onUpdateCommandList(String message, CallbackInfo ci) {
19+
for (String key : CommandCompat.clientCommands.keySet()) {
20+
if (!commandsNames.contains(key)) {
21+
commandsNames.add(key);
22+
}
23+
}
24+
}
25+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.fox2code.foxloader.client.mixins;
2+
3+
import com.fox2code.foxloader.loader.ClientMod;
4+
import com.fox2code.foxloader.registry.CommandCompat;
5+
import net.minecraft.src.client.gui.GuiTextField;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.injection.At;
8+
import org.spongepowered.asm.mixin.injection.Redirect;
9+
10+
import java.util.Map;
11+
12+
@Mixin(GuiTextField.class)
13+
public class MixinGuiTextField {
14+
@Redirect(method = "textboxKeyTyped", at = @At(value = "INVOKE",
15+
target = "Ljava/lang/String;length()I", ordinal = 0))
16+
public int checkClientCommand(String instance) {
17+
if (!instance.startsWith("/"))
18+
return instance.length();
19+
for (Map.Entry<String, CommandCompat> entry :
20+
CommandCompat.clientCommands.entrySet()) {
21+
boolean match = instance.startsWith("/" + entry.getKey() + " ") ||
22+
instance.equals("/" + entry.getKey());
23+
if (!match) {
24+
for (String alias : entry.getValue().getAliases()) {
25+
match = instance.startsWith("/" + alias + " ") ||
26+
instance.equals("/" + alias);
27+
if (match) break;
28+
}
29+
}
30+
31+
if (match) {
32+
entry.getValue().onExecute(instance.split(" "),
33+
ClientMod.getLocalNetworkPlayer());
34+
return 0;
35+
}
36+
}
37+
return instance.length();
38+
}
39+
}

client/src/main/java/com/fox2code/foxloader/spark/FoxLoaderClientSparkPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ public FoxLoaderClientSparkPlugin() {
1313

1414
@Override
1515
public Stream<? extends CommandSender> getCommandSenders() {
16-
return Stream.of(new FoxLoaderSparkCommandSender(ClientMod.getLocalNetworkPlayer()));
16+
return Stream.of(new FoxLoaderSparkCommandSender(ClientMod.getLocalNetworkPlayer(), true));
1717
}
1818
}

client/src/main/resources/foxloader.client.mixins.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"MixinBlockFire",
99
"MixinChunk",
1010
"MixinChunkBlockMap",
11+
"MixinCommandHelperGUI",
1112
"MixinContainerCreative",
1213
"MixinEntity",
1314
"MixinEntityClientPlayerMP",
@@ -20,6 +21,7 @@
2021
"MixinGuiContainer",
2122
"MixinGuiDebug",
2223
"MixinGuiMainMenu",
24+
"MixinGuiTextField",
2325
"MixinItem",
2426
"MixinItemStack",
2527
"MixinMinecraft",

common/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies {
99
api 'org.semver4j:semver4j:4.3.0'
1010
api 'org.spongepowered:mixin:0.8.5'
1111
api 'com.github.LlamaLad7.MixinExtras:mixinextras-common:0.2.0-beta.7'
12+
api 'org.luaj:luaj-jse:3.0.1'
1213

1314
// Mixin dependencies
1415
api 'com.google.code.gson:gson:2.2.4'

common/src/main/java/com/fox2code/foxloader/launcher/DependencyHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class DependencyHelper {
2929
GSON_DEPENDENCY, new Dependency("com.google.guava:guava:21.0", MAVEN_CENTRAL, "com.google.common.io.Files"),
3030
new Dependency("org.semver4j:semver4j:4.3.0", MAVEN_CENTRAL, "org.semver4j.Semver"),
3131
new Dependency("org.apache.commons:commons-lang3:3.3.2", MAVEN_CENTRAL, "org.apache.commons.lang3.tuple.Pair"),
32+
new Dependency("org.luaj:luaj-jse:3.0.1", MAVEN_CENTRAL, "org.luaj.vm2.Globals"),
3233
new Dependency("org.spongepowered:mixin:0.8.5", SPONGE_POWERED, "org.spongepowered.asm.mixin.Mixins"),
3334
new Dependency("com.github.LlamaLad7.MixinExtras:mixinextras-common:0.2.0-beta.7",
3435
JITPACK, "com.llamalad7.mixinextras.MixinExtrasBootstrap"),

common/src/main/java/com/fox2code/foxloader/loader/Mod.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public final Object getConfigObject() {
3535
/**
3636
* @return Object to use for the config screen.
3737
*/
38-
public final Object getConfigFolder() {
38+
public final File getConfigFolder() {
3939
File configFolder = this.getModContainer().configFolder;
4040
if (!configFolder.isDirectory() && !configFolder.mkdirs()) {
4141
throw new IOError(new IOException("Can't create " + configFolder.getPath()));

common/src/main/java/com/fox2code/foxloader/loader/ModContainer.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package com.fox2code.foxloader.loader;
22

33
import com.fox2code.foxloader.launcher.FoxLauncher;
4+
import com.fox2code.foxloader.loader.lua.LuaVMHelper;
45
import com.fox2code.foxloader.loader.packet.ClientHello;
56
import com.fox2code.foxloader.loader.transformer.PreClassTransformer;
67
import com.fox2code.foxloader.network.NetworkPlayer;
78
import com.fox2code.foxloader.registry.RegisteredItemStack;
89
import org.semver4j.Semver;
910

1011
import java.io.File;
12+
import java.io.IOException;
1113
import java.io.InputStream;
1214
import java.io.InputStreamReader;
1315
import java.nio.charset.StandardCharsets;
@@ -145,6 +147,13 @@ void applyMod(boolean client) throws ReflectiveOperationException {
145147
logger.log(Level.WARNING, "Your mod don't have a /assets/" + id + "/lang/en_US.lang");
146148
}
147149
} catch (Throwable ignored) {}
150+
if (file.getName().endsWith(".lua") && this.commonMod == null) {
151+
try {
152+
this.commonMod = LuaVMHelper.loadMod(this);
153+
} catch (IOException e) {
154+
logger.log(Level.SEVERE, "Unable to load " + file.getName(), e);
155+
}
156+
}
148157
}
149158

150159
void loadLanguageTo(String lang, Properties target) {

common/src/main/java/com/fox2code/foxloader/loader/ModLoader.java

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fox2code.foxloader.commands.WorldSet;
55
import com.fox2code.foxloader.launcher.*;
66
import com.fox2code.foxloader.launcher.utils.SourceUtil;
7+
import com.fox2code.foxloader.loader.lua.LuaVMHelper;
78
import com.fox2code.foxloader.loader.packet.ServerHello;
89
import com.fox2code.foxloader.loader.rebuild.ClassDataProvider;
910
import com.fox2code.foxloader.network.NetworkPlayer;
@@ -14,13 +15,15 @@
1415
import org.jetbrains.annotations.NotNull;
1516
import org.semver4j.Semver;
1617

17-
import java.io.File;
18-
import java.io.IOException;
18+
import java.io.*;
1919
import java.net.MalformedURLException;
20+
import java.nio.charset.StandardCharsets;
21+
import java.nio.file.Files;
2022
import java.util.*;
2123
import java.util.jar.Attributes;
2224
import java.util.jar.JarFile;
2325
import java.util.jar.Manifest;
26+
import java.util.logging.Level;
2427
import java.util.logging.Logger;
2528

2629
public class ModLoader extends Mod {
@@ -102,16 +105,24 @@ static void initializeModdedInstance(boolean client) {
102105
}
103106
for (File mod : Objects.requireNonNull(mods.listFiles(
104107
(dir, name) -> name.endsWith(".jar")))) {
105-
loadModContainerFrom(mod, false);
108+
loadModContainerFromJar(mod, false);
109+
}
110+
for (File mod : Objects.requireNonNull(mods.listFiles(
111+
(dir, name) -> name.endsWith(".lua")))) {
112+
loadModContainerFromLua(mod);
106113
}
107114
for (File mod : Objects.requireNonNull(modsVersioned.listFiles(
108115
(dir, name) -> name.endsWith(".jar")))) {
109-
loadModContainerFrom(mod, false);
116+
loadModContainerFromJar(mod, false);
117+
}
118+
for (File mod : Objects.requireNonNull(modsVersioned.listFiles(
119+
(dir, name) -> name.endsWith(".lua")))) {
120+
loadModContainerFromLua(mod);
110121
}
111122
}
112123
// Inject mod is used by the gradle plugin to load dev mod
113124
if (DEV_MODE && INJECT_MOD != null && !INJECT_MOD.isEmpty()) {
114-
loadModContainerFrom(new File(INJECT_MOD).getAbsoluteFile(), true);
125+
loadModContainerFromJar(new File(INJECT_MOD).getAbsoluteFile(), true);
115126
}
116127
if (!modContainers.containsKey("spark") && !disableSpark &&
117128
DependencyHelper.loadDependencySafe(DependencyHelper.sparkDependency)) {
@@ -168,7 +179,7 @@ public void onPostInit() {
168179
CommandCompat.registerCommand(new WorldReplace());
169180
}
170181

171-
private static void loadModContainerFrom(File file, boolean injected) {
182+
private static void loadModContainerFromJar(File file, boolean injected) {
172183
Manifest manifest;
173184
try (JarFile jarFile = new JarFile(file)) {
174185
manifest = jarFile.getManifest();
@@ -244,6 +255,83 @@ private static void loadModContainerFrom(File file, boolean injected) {
244255
}
245256
}
246257

258+
private static final String MOD_ID_LUA_PREFIX = "-- modId: ";
259+
private static final String MOD_NAME_LUA_PREFIX = "-- modName: ";
260+
private static final String MOD_VERSION_LUA_PREFIX = "-- version: ";
261+
private static final String MOD_DESC_LUA_PREFIX = "-- description: ";
262+
private static final String MOD_UNOFFICIAL_LUA_PREFIX = "-- unofficial: ";
263+
264+
private static void loadModContainerFromLua(File file) {
265+
String id = null;
266+
String name = null;
267+
String version = null;
268+
String desc = null;
269+
boolean unofficial = false;
270+
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
271+
Files.newInputStream(file.toPath()), StandardCharsets.UTF_8))) {
272+
String line;
273+
while ((line = bufferedReader.readLine()) != null
274+
&& line.startsWith("-- ")) {
275+
if (line.startsWith(MOD_ID_LUA_PREFIX)) {
276+
id = line.substring(MOD_ID_LUA_PREFIX.length());
277+
} else if (line.startsWith(MOD_NAME_LUA_PREFIX)) {
278+
name = line.substring(MOD_NAME_LUA_PREFIX.length());
279+
} else if (line.startsWith(MOD_VERSION_LUA_PREFIX)) {
280+
version = line.substring(MOD_VERSION_LUA_PREFIX.length());
281+
} else if (line.startsWith(MOD_DESC_LUA_PREFIX)) {
282+
desc = line.substring(MOD_DESC_LUA_PREFIX.length());
283+
} else if (line.startsWith(MOD_UNOFFICIAL_LUA_PREFIX)) {
284+
unofficial = Boolean.parseBoolean(line.substring(
285+
MOD_UNOFFICIAL_LUA_PREFIX.length()));
286+
}
287+
}
288+
} catch (IOException ioe) {
289+
ioe.printStackTrace();
290+
return;
291+
}
292+
293+
if (id == null || id.isEmpty()) {
294+
foxLoader.logger.warning("Unable to load " + file.getName() +
295+
" because it doesn't have a mod-id (Is it a core mod?)");
296+
return;
297+
}
298+
if (FOX_LOADER_MOD_ID.equals(id) || "minecraft".equals(id) || "reindev".equals(id) || "null".equals(id)) {
299+
foxLoader.logger.warning("Unable to load " + file.getName() +
300+
" because it used the reserved mod id: " + id);
301+
return;
302+
}
303+
if (name == null || name.isEmpty()) {
304+
name = id.substring(0, 1).toLowerCase(Locale.ROOT) + id.substring(1);
305+
}
306+
if (version == null || (version = version.trim()).isEmpty()) {
307+
version = "1.0";
308+
}
309+
Semver semver = "1.0".equals(version) || "1.0.0".equals(version) ?
310+
INITIAL_SEMVER : Semver.parse(version);
311+
if (semver == null) {
312+
int verExt = version.indexOf('-');
313+
if (verExt != -1) {
314+
version = version.substring(0, verExt);
315+
}
316+
semver = Semver.coerce(version);
317+
}
318+
if (semver == null) {
319+
semver = INITIAL_SEMVER;
320+
}
321+
322+
if (desc == null || desc.isEmpty()) {
323+
desc = "...";
324+
}
325+
ModContainer modContainer = modContainers.get(id);
326+
if (modContainer != null) {
327+
foxLoader.logger.warning("Unable to load " + file.getName() + " because " +
328+
modContainer.file.getName() + " already uses the same mod id: " + id);
329+
return;
330+
}
331+
modContainer = new ModContainer(file, id, name, version, semver, desc, null, unofficial, false);
332+
modContainers.put(id, modContainer);
333+
}
334+
247335
public static boolean checkSemVerMismatch(String value, String accept) {
248336
return accept == null || !checkSemVerMatch(value, accept);
249337
}

0 commit comments

Comments
 (0)