mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-12 03:37:27 +07:00
Merge branch 'plugins' of https://github.com/Anuken/Mindustry
This commit is contained in:
commit
76f11099b7
@ -16,6 +16,8 @@ buildscript{
|
||||
|
||||
allprojects{
|
||||
version = 'release'
|
||||
apply plugin: 'maven'
|
||||
group = 'com.github.Anuken'
|
||||
|
||||
ext{
|
||||
versionNumber = '4'
|
||||
|
@ -69,6 +69,7 @@ players.single = {0} player online
|
||||
server.closing = [accent]Closing server...
|
||||
server.kicked.kick = You have been kicked from the server!
|
||||
server.kicked.serverClose = Server closed.
|
||||
server.kicked.vote = You have been vote-kicked. Goodbye.
|
||||
server.kicked.clientOutdated = Outdated client! Update your game!
|
||||
server.kicked.serverOutdated = Outdated server! Ask the host to update!
|
||||
server.kicked.banned = You are banned on this server.
|
||||
|
@ -121,6 +121,8 @@ public class Vars implements Loadable{
|
||||
public static FileHandle tmpDirectory;
|
||||
/** data subdirectory used for saves */
|
||||
public static FileHandle saveDirectory;
|
||||
/** data subdirectory used for plugins */
|
||||
public static FileHandle pluginDirectory;
|
||||
/** old map file extension, for conversion */
|
||||
public static final String oldMapExtension = "mmap";
|
||||
/** map file extension */
|
||||
@ -237,6 +239,7 @@ public class Vars implements Loadable{
|
||||
mapPreviewDirectory = dataDirectory.child("previews/");
|
||||
saveDirectory = dataDirectory.child("saves/");
|
||||
tmpDirectory = dataDirectory.child("tmp/");
|
||||
pluginDirectory = dataDirectory.child("plugins/");
|
||||
|
||||
maps.load();
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import io.anuke.arc.collection.IntSet;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.math.RandomXS128;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.CommandHandler.*;
|
||||
import io.anuke.arc.util.io.ReusableByteInStream;
|
||||
import io.anuke.arc.util.serialization.Base64Coder;
|
||||
import io.anuke.mindustry.Vars;
|
||||
@ -151,12 +152,35 @@ public class NetClient implements ApplicationListener{
|
||||
throw new ValidateException(player, "Player has sent a message above the text limit.");
|
||||
}
|
||||
|
||||
//server console logging
|
||||
Log.info("&y{0}: &lb{1}", player.name, message);
|
||||
//check if it's a command
|
||||
CommandResponse response = netServer.clientCommands.handleMessage(message, player);
|
||||
if(response.type == ResponseType.noCommand){ //no command to handle
|
||||
//server console logging
|
||||
Log.info("&y{0}: &lb{1}", player.name, message);
|
||||
|
||||
//invoke event for all clients but also locally
|
||||
//this is required so other clients get the correct name even if they don't know who's sending it yet
|
||||
Call.sendMessage(message, colorizeName(player.id, player.name), player);
|
||||
//invoke event for all clients but also locally
|
||||
//this is required so other clients get the correct name even if they don't know who's sending it yet
|
||||
Call.sendMessage(message, colorizeName(player.id, player.name), player);
|
||||
}else{
|
||||
//log command to console but with brackets
|
||||
Log.info("<&y{0}: &lm{1}&lg>", player.name, message);
|
||||
|
||||
//a command was sent, now get the output
|
||||
if(response.type != ResponseType.valid){
|
||||
String text;
|
||||
|
||||
//send usage
|
||||
if(response.type == ResponseType.manyArguments){
|
||||
text = "[scarlet]Too many arguments. Usage:[lightgray] " + response.command.text + "[gray] " + response.command.paramText;
|
||||
}else if(response.type == ResponseType.fewArguments){
|
||||
text = "[scarlet]Too few arguments. Usage:[lightgray] " + response.command.text + "[gray] " + response.command.paramText;
|
||||
}else{ //unknown command
|
||||
text = "[scarlet]Unknown command. Check [lightgray]/help[scarlet].";
|
||||
}
|
||||
|
||||
player.sendMessage(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String colorizeName(int id, String name){
|
||||
|
@ -4,14 +4,14 @@ import io.anuke.annotations.Annotations.Loc;
|
||||
import io.anuke.annotations.Annotations.Remote;
|
||||
import io.anuke.arc.ApplicationListener;
|
||||
import io.anuke.arc.Events;
|
||||
import io.anuke.arc.collection.IntMap;
|
||||
import io.anuke.arc.collection.ObjectSet;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.Colors;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.CommandHandler.*;
|
||||
import io.anuke.arc.util.io.*;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
@ -47,6 +47,7 @@ public class NetServer implements ApplicationListener{
|
||||
private final static float correctDist = 16f;
|
||||
|
||||
public final Administration admins = new Administration();
|
||||
public final CommandHandler clientCommands = new CommandHandler("/");
|
||||
|
||||
/** Maps connection IDs to players. */
|
||||
private IntMap<Player> connections = new IntMap<>();
|
||||
@ -192,6 +193,132 @@ public class NetServer implements ApplicationListener{
|
||||
if(player == null) return;
|
||||
RemoteReadServer.readPacket(packet.writeBuffer, packet.type, player);
|
||||
});
|
||||
|
||||
registerCommands();
|
||||
}
|
||||
|
||||
private void registerCommands(){
|
||||
clientCommands.<Player>register("help", "[page]", "Lists all commands.", (args, player) -> {
|
||||
if(args.length > 0 && !Strings.canParseInt(args[0])){
|
||||
player.sendMessage("[scarlet]'page' must be a number.");
|
||||
return;
|
||||
}
|
||||
int commandsPerPage = 6;
|
||||
int page = args.length > 0 ? Strings.parseInt(args[0]) : 0;
|
||||
int pages = Mathf.ceil((float)clientCommands.getCommandList().size / commandsPerPage);
|
||||
|
||||
if(page > pages || page < 0){
|
||||
player.sendMessage("[scarlet]'page' must be a number between[orange] 0[] and[orange] " + pages + "[].");
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(Strings.format("[orange]-- Command Page[lightgray] {0}[gray]/[lightgray]{1}[orange] --\n\n", page, commandsPerPage));
|
||||
|
||||
for(int i = commandsPerPage * page; i < Math.min(commandsPerPage * (page + 1), clientCommands.getCommandList().size); i++){
|
||||
Command command = clientCommands.getCommandList().get(i);
|
||||
result.append("[orange] ").append(command.text).append("[white] ").append(command.paramText).append("[lightgray] - ").append(command.description).append("\n");
|
||||
}
|
||||
player.sendMessage(result.toString());
|
||||
});
|
||||
|
||||
//duration of a a kick in seconds
|
||||
int kickDuration = 10 * 60;
|
||||
|
||||
class VoteSession{
|
||||
Player target;
|
||||
ObjectSet<String> voted = new ObjectSet<>();
|
||||
ObjectMap<Player, VoteSession> map;
|
||||
Timer.Task task;
|
||||
int votes;
|
||||
|
||||
public VoteSession(ObjectMap<Player, VoteSession> map, Player target){
|
||||
this.target = target;
|
||||
this.map = map;
|
||||
this.task = Timer.schedule(() -> {
|
||||
if(!checkPass()){
|
||||
Call.sendMessage(Strings.format("[lightgray]Vote failed. Not enough votes to kick[orange] {0}[lightgray].", target.name));
|
||||
}
|
||||
map.remove(target);
|
||||
task.cancel();
|
||||
}, 60 * 1.5f);
|
||||
}
|
||||
|
||||
boolean checkPass(){
|
||||
if(votes >= votesRequired() && target.isAdded() && target.con.isConnected()){
|
||||
Call.sendMessage(Strings.format("[orange]Vote passed.[scarlet] {0}[orange] will be kicked from the server.", target.name));
|
||||
admins.getInfo(target.uuid).lastKicked = Time.millis() + kickDuration*1000;
|
||||
kick(target.con.id, KickReason.vote);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//cooldown between votes
|
||||
int voteTime = 60 * 10;
|
||||
Timekeeper vtime = new Timekeeper(voteTime);
|
||||
//current kick sessions
|
||||
ObjectMap<Player, VoteSession> currentlyKicking = new ObjectMap<>();
|
||||
|
||||
clientCommands.<Player>register("votekick", "[player]", "Vote to kick a player, with a cooldown.", (args, player) -> {
|
||||
if(playerGroup.size() < 3){
|
||||
player.sendMessage("[scarlet]At least 3 players are needed to start a votekick.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentlyKicking.values().toArray().contains(v -> v.voted.contains(player.uuid) || v.voted.contains(admins.getInfo(player.uuid).lastIP))){
|
||||
player.sendMessage("[scarlet]You've already voted. Sit down.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(args.length == 0){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[orange]Players to kick: \n");
|
||||
for(Player p : playerGroup.all()){
|
||||
if(p.isAdmin || p.con == null || p == player) continue;
|
||||
|
||||
builder.append("[lightgray] ").append(p.name).append("[accent] (#").append(p.con.id).append(")\n");
|
||||
}
|
||||
player.sendMessage(builder.toString());
|
||||
}else{
|
||||
Player found;
|
||||
if(args[0].length() > 1 && args[0].startsWith("#") && Strings.canParseInt(args[0].substring(1))){
|
||||
int id = Strings.parseInt(args[0].substring(1));
|
||||
found = playerGroup.find(p -> p.con != null && p.con.id == id);
|
||||
}else{
|
||||
found = playerGroup.find(p -> p.name.equalsIgnoreCase(args[0]));
|
||||
}
|
||||
|
||||
if(found != null){
|
||||
if(player == found){
|
||||
player.sendMessage("[scarlet]If you're interested in kicking yourself, just leave.");
|
||||
}else if(found.isAdmin){
|
||||
player.sendMessage("[scarlet]Did you really expect to be able to kick an admin?");
|
||||
}else{
|
||||
if(!currentlyKicking.containsKey(found) && !vtime.get()){
|
||||
player.sendMessage("[scarlet]You must wait " + voteTime/60 + " minutes between votekicks.");
|
||||
return;
|
||||
}
|
||||
|
||||
VoteSession session = currentlyKicking.getOr(found, () -> new VoteSession(currentlyKicking, found));
|
||||
session.votes ++;
|
||||
session.voted.addAll(player.uuid, admins.getInfo(player.uuid).lastIP);
|
||||
|
||||
Call.sendMessage(Strings.format("[orange]{0}[lightgray] has voted to kick[orange] {1}[].[accent] ({2}/{3})\n[lightgray]Type[orange] /votekick #{4}[] to agree.",
|
||||
player.name, found.name, session.votes, votesRequired(), found.con.id));
|
||||
session.checkPass();
|
||||
vtime.reset();
|
||||
}
|
||||
}else{
|
||||
player.sendMessage("[scarlet]No player[orange]'" + args[0] + "'[scarlet] found.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int votesRequired(){
|
||||
return playerGroup.size() * 2 / 3;
|
||||
}
|
||||
|
||||
public Team assignTeam(Player current, Iterable<Player> players){
|
||||
@ -423,10 +550,10 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
Player player = connections.get(con.id);
|
||||
|
||||
if(player != null && (reason == KickReason.kick || reason == KickReason.banned) && player.uuid != null){
|
||||
if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){
|
||||
PlayerInfo info = admins.getInfo(player.uuid);
|
||||
info.timesKicked++;
|
||||
info.lastKicked = Time.millis();
|
||||
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
|
||||
}
|
||||
|
||||
Call.onKick(connection, reason);
|
||||
|
@ -774,6 +774,10 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
//region utility methods
|
||||
|
||||
public void sendMessage(String text){
|
||||
Call.sendMessage(con.id, text, null, null);
|
||||
}
|
||||
|
||||
/** Resets all values of the player. */
|
||||
public void reset(){
|
||||
resetNoAdd();
|
||||
|
@ -7,7 +7,6 @@ import io.anuke.arc.collection.*;
|
||||
import static io.anuke.mindustry.Vars.headless;
|
||||
|
||||
public class Administration{
|
||||
|
||||
/** All player info. Maps UUIDs to info. This persists throughout restarts. */
|
||||
private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>();
|
||||
private Array<String> bannedIPs = new Array<>();
|
||||
|
@ -14,7 +14,7 @@ public class Packets{
|
||||
|
||||
public enum KickReason{
|
||||
kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick,
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose;
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote;
|
||||
|
||||
public final boolean quiet;
|
||||
|
||||
|
@ -22,11 +22,10 @@ public class MindustryServer implements ApplicationListener{
|
||||
Vars.loadSettings();
|
||||
Vars.init();
|
||||
content.createContent();
|
||||
content.init();
|
||||
|
||||
Core.app.addListener(logic = new Logic());
|
||||
Core.app.addListener(netServer = new NetServer());
|
||||
Core.app.addListener(new ServerControl(args));
|
||||
|
||||
content.init();
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ import io.anuke.mindustry.maps.*;
|
||||
import io.anuke.mindustry.net.Administration.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.server.plugin.*;
|
||||
import io.anuke.mindustry.server.plugin.Plugins.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
|
||||
import java.io.*;
|
||||
@ -40,6 +42,7 @@ public class ServerControl implements ApplicationListener{
|
||||
|
||||
private final CommandHandler handler = new CommandHandler("");
|
||||
private final FileHandle logFolder = Core.files.local("logs/");
|
||||
private final Plugins plugins = new Plugins();
|
||||
|
||||
private FileHandle currentLogFile;
|
||||
private boolean inExtraRound;
|
||||
@ -107,6 +110,9 @@ public class ServerControl implements ApplicationListener{
|
||||
Effects.setScreenShakeProvider((a, b) -> {});
|
||||
Effects.setEffectProvider((a, b, c, d, e, f) -> {});
|
||||
|
||||
//load plugins
|
||||
plugins.load();
|
||||
|
||||
registerCommands();
|
||||
|
||||
Core.app.post(() -> {
|
||||
@ -118,7 +124,7 @@ public class ServerControl implements ApplicationListener{
|
||||
}
|
||||
|
||||
for(String s : commands){
|
||||
Response response = handler.handleMessage(s);
|
||||
CommandResponse response = handler.handleMessage(s);
|
||||
if(response.type != ResponseType.valid){
|
||||
err("Invalid command argument sent: '{0}': {1}", s, response.type.name());
|
||||
err("Argument usage: &lc<command-1> <command1-args...>,<command-2> <command-2-args2...>");
|
||||
@ -128,6 +134,7 @@ public class ServerControl implements ApplicationListener{
|
||||
});
|
||||
|
||||
customMapDirectory.mkdirs();
|
||||
pluginDirectory.mkdirs();
|
||||
|
||||
Thread thread = new Thread(this::readCommands, "Server Controls");
|
||||
thread.setDaemon(true);
|
||||
@ -166,6 +173,13 @@ public class ServerControl implements ApplicationListener{
|
||||
}
|
||||
});
|
||||
|
||||
//initialize plugins
|
||||
plugins.each(Plugin::init);
|
||||
|
||||
if(!plugins.all().isEmpty()){
|
||||
info("&lc{0} plugins loaded.", plugins.all().size);
|
||||
}
|
||||
|
||||
info("&lcServer loaded. Type &ly'help'&lc for help.");
|
||||
System.out.print("> ");
|
||||
|
||||
@ -306,6 +320,31 @@ public class ServerControl implements ApplicationListener{
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("plugins", "Display all loaded plugins.", arg -> {
|
||||
if(!plugins.all().isEmpty()){
|
||||
info("Maps:");
|
||||
for(LoadedPlugin plugin : plugins.all()){
|
||||
info(" &ly{0} &lcv{1}", plugin.meta.name, plugin.meta.version);
|
||||
}
|
||||
}else{
|
||||
info("No plugins found.");
|
||||
}
|
||||
info("&lyPlugin directory: &lb&fi{0}", pluginDirectory.file().getAbsoluteFile().toString());
|
||||
});
|
||||
|
||||
handler.register("plugin", "<name...>", "Display information about a loaded plugin.", arg -> {
|
||||
LoadedPlugin plugin = plugins.all().find(p -> p.meta.name.equalsIgnoreCase(arg[0]));
|
||||
if(plugin != null){
|
||||
info("Name: &ly{0}", plugin.meta.name);
|
||||
info("Version: &ly{0}", plugin.meta.version);
|
||||
info("Author: &ly{0}", plugin.meta.author);
|
||||
info("Path: &ly{0}", plugin.jarFile.path());
|
||||
info("Description: &ly{0}", plugin.meta.description);
|
||||
}else{
|
||||
info("No plugin with name &ly'{0}'&lg found.");
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("say", "<message...>", "Send a message to all players.", arg -> {
|
||||
if(!state.is(State.playing)){
|
||||
err("Not hosting. Host a game first.");
|
||||
@ -644,12 +683,15 @@ public class ServerControl implements ApplicationListener{
|
||||
}
|
||||
});
|
||||
|
||||
handler.register("gc", "Trigger a grabage collection. Testing onlu.", arg -> {
|
||||
handler.register("gc", "Trigger a grabage collection. Testing only.", arg -> {
|
||||
int pre = (int)(Core.app.getJavaHeap() / 1024 / 1024);
|
||||
System.gc();
|
||||
int post = (int)(Core.app.getJavaHeap() / 1024 / 1024);
|
||||
info("&ly{0}&lg MB collected. Memory usage now at &ly{1}&lg MB.", pre - post, post);
|
||||
});
|
||||
|
||||
plugins.each(p -> p.registerServerCommands(handler));
|
||||
plugins.each(p -> p.registerClientCommands(netServer.clientCommands));
|
||||
}
|
||||
|
||||
private void readCommands(){
|
||||
@ -662,7 +704,7 @@ public class ServerControl implements ApplicationListener{
|
||||
}
|
||||
|
||||
private void handleCommandString(String line){
|
||||
Response response = handler.handleMessage(line);
|
||||
CommandResponse response = handler.handleMessage(line);
|
||||
|
||||
if(response.type == ResponseType.unknownCommand){
|
||||
|
||||
|
21
server/src/io/anuke/mindustry/server/plugin/Plugin.java
Normal file
21
server/src/io/anuke/mindustry/server/plugin/Plugin.java
Normal file
@ -0,0 +1,21 @@
|
||||
package io.anuke.mindustry.server.plugin;
|
||||
|
||||
import io.anuke.arc.util.*;
|
||||
|
||||
public abstract class Plugin{
|
||||
|
||||
/** Called after all plugins have been created and commands have been registered.*/
|
||||
public void init(){
|
||||
|
||||
}
|
||||
|
||||
/** Register any commands to be used on the server side, e.g. from the console. */
|
||||
public void registerServerCommands(CommandHandler handler){
|
||||
|
||||
}
|
||||
|
||||
/** Register any commands to be used on the client side, e.g. sent from an in-game player.. */
|
||||
public void registerClientCommands(CommandHandler handler){
|
||||
|
||||
}
|
||||
}
|
82
server/src/io/anuke/mindustry/server/plugin/Plugins.java
Normal file
82
server/src/io/anuke/mindustry/server/plugin/Plugins.java
Normal file
@ -0,0 +1,82 @@
|
||||
package io.anuke.mindustry.server.plugin;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.io.*;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.pluginDirectory;
|
||||
|
||||
public class Plugins{
|
||||
private Array<LoadedPlugin> loaded = new Array<>();
|
||||
|
||||
/** Loads all plugins from the folder, but does call any methods on them.*/
|
||||
public void load(){
|
||||
for(FileHandle file : pluginDirectory.list()){
|
||||
if(!file.extension().equals("jar")) continue;
|
||||
|
||||
try{
|
||||
loaded.add(loadPlugin(file));
|
||||
}catch(IllegalArgumentException ignored){
|
||||
}catch(Exception e){
|
||||
Log.err("Failed to load plugin file {0}. Skipping.", file);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return all loaded plugins. */
|
||||
public Array<LoadedPlugin> all(){
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/** Iterates through each plugin.*/
|
||||
public void each(Consumer<Plugin> cons){
|
||||
loaded.each(p -> cons.accept(p.plugin));
|
||||
}
|
||||
|
||||
private LoadedPlugin loadPlugin(FileHandle jar) throws Exception{
|
||||
FileHandle zip = new ZipFileHandle(jar);
|
||||
|
||||
FileHandle metaf = zip.child("plugin.json");
|
||||
if(!metaf.exists()){
|
||||
Log.warn("Plugin {0} doesn't have a 'plugin.json' file, skipping.", jar);
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
PluginMeta meta = JsonIO.read(PluginMeta.class, metaf.readString());
|
||||
|
||||
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
|
||||
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(classLoader, jar.file().toURI().toURL());
|
||||
|
||||
Class<?> main = Class.forName(meta.main);
|
||||
return new LoadedPlugin(jar, zip, (Plugin)main.newInstance(), meta);
|
||||
}
|
||||
|
||||
/** Represents a plugin that has been loaded from a jar file.*/
|
||||
public static class LoadedPlugin{
|
||||
public final FileHandle jarFile;
|
||||
public final FileHandle zipRoot;
|
||||
public final Plugin plugin;
|
||||
public final PluginMeta meta;
|
||||
|
||||
public LoadedPlugin(FileHandle jarFile, FileHandle zipRoot, Plugin plugin, PluginMeta meta){
|
||||
this.zipRoot = zipRoot;
|
||||
this.jarFile = jarFile;
|
||||
this.plugin = plugin;
|
||||
this.meta = meta;
|
||||
}
|
||||
}
|
||||
|
||||
/** Plugin metadata information.*/
|
||||
public static class PluginMeta{
|
||||
public String name, author, main, description;
|
||||
public String version;
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ def use = { String name ->
|
||||
|
||||
def properties = new Properties()
|
||||
|
||||
if(new File(settingsDir, 'local.properties').exists()){
|
||||
if(new File(settingsDir, 'local.properties').exists() && System.getenv("JITPACK") != "true"){
|
||||
properties.load(new File(settingsDir, 'local.properties').newDataInputStream())
|
||||
|
||||
if(properties.containsKey("sdk.dir")){
|
||||
|
Loading…
Reference in New Issue
Block a user