Compare commits

...

1 Commits

Author SHA1 Message Date
4eeb6c64a3 First upload 2015-09-04 19:56:58 +02:00
10 changed files with 817 additions and 10 deletions

50
.gitignore vendored
View File

@ -1,8 +1,42 @@
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
# Eclipse stuff
/.classpath
/.project
/.settings
# netbeans
/nbproject
nb-configuration.xml
# maven
/target
# vim
.*.sw[a-p]
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# various other potential build files
/build
/bin
/dist
/manifest.mf
*.log
# Mac filesystem dust
.DS_Store
# intellij
*.iml
*.ipr
*.iws
.idea/
# Gradle
.gradle
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

View File

@ -1,2 +1,7 @@
# FastLogin
Checks if a minecraft player has a valid paid account. If so, they can skip offline authentification.
# mcMMOExtras
A visual boss bar Bukkit plugin for mcMMO that keeps people entertained and encourages them to want to level up.
See
* http://dev.bukkit.org/bukkit-plugins/mcmmoextras/
* http://www.curse.com/bukkit-plugins/minecraft/mcmmoextras

198
config-from-autoin.txt Normal file
View File

@ -0,0 +1,198 @@
Options:
ConfigurationVersion_DO_NOT_CHANGE_THIS: 6
# 0 -> unlimited/as needed.
NettyThreads: 0
# DON'T USE THAT IF YOU CAN USE SOCKETS, Enable when you can't use sockets but you want use that on bungee server, it isn't good as sockets, but it should not explode. NOTE: You NEED MySQL for that, and this is slower and may by VERY buggy on first join.
UseChannels: false
# Time in ms between trying connect LISTENER to HANLDER socket, on linux using smaller times can cause some weird problems.
ReTryConnectSocketTime: 250
# where AutoIn should store exceptions, enabled login, whitelists, cache and other player settings.
SaveData:
Type: MySQL
MySQL:
Host: localhost
Port: 3306
User: minecraft
Pass:
Database: minecraft
Prefix: AutoIn_
SQLite:
File: AutoIn_PlayerOptions.db
# Both
Both:
Dependencies:
# If you have one of that plugin, you can force AutoIn to don't use them even if they can be used. (change to true)
ForceDisable:
ProtocolLib: false
Skript: false
AuthMe: false
LogIt: false
XAuth: false
LoginSecurity: false
Listeners:
# you can try disable that and manualy configure priority below if auto-login will stop work after update of auth pluhin. PS: tell me about that problem!
GetFromModule: true
PlayerLoginEvent: LOWEST
PlayerJoinEvent: LOWEST
PlayerQuitEvent: MONITOR
Players:
# no more /login commands for premium users! Enable only on servers where you have auth plugin!
AutoLogin: true
# if true autoin will try fix skins, you can disable that if you want use other plugin. (some plugins may still not work)
FixSkins: true
# if true, then all players are exception by default, they can use /ai IAmNowPremium to remove exception flag (NOTE: they will lose all data after login as premium if you have fixedUUIDs set to false)
NegateExceptions: false
# if true, then even premium players needs to register. PS: You can make registration optional, see wiki: https://github.com/GotoFinal/AutoIn/wiki/Registration
Registration: true
# A.K.A. SwitchMode, If you disable that, only old cracked players will be able to join. New cracked players will be kicked from server. Good if you want switch from offline-mode to online-mode without losing players!
AllowNewCrackedPlayers: true
# if true, then everyone have UUID generated from nickname
FixedUUID: false
# WhiteList that works only on cracked players, black-list works even if this is disabled.
CrackedWhiteListEnabled: false
ForceLogin:
# Allow use RegEx in nicknames list, like 'Test\\d{1,3}'
UseRegEx: false
# Nicknames from this list don't need use login or register command, use to supprot mods like buildcraft and others
Nicknames:
- SomeNicknameThatDoNotNeedUseLoginOrRegisterCommandEvenIfHeDoNotHavePremium
Sessions:
# If enabled, plugin will remember premium players with their IP number, when servers will be down, premium players can be still auto-logged if IP will be valid.
Enabled: true
# If true, then players with valid session can join even if mojang server are down and without using password. PS: You can enable this and ServerProtect, then registered players OR players with valid session can join.
AsServerProtect: true
# time in easy format, 1w = 1 week, 1d = 1 day, 1d5h12s -> 1 day, 5 hours, 12 seconds
Expires: 1d
# If true sessions aren't saved on reload.
ExpiresOnRestart: true
Protections:
# If enabled, when new cracked player join to game he will get special protection "flag", that work like exception-player. so even if someone buy premium account with this same nickname, he will be still tract as cracked player.
# If player will buy premium, he must disable protection using "IamNowPremium" command, or admin must disable it manually, using API or /setProtection command.
CrackedPlayersNicknames:
Enabled: true
# If disabled, player must use "ProtectMe" command, or protection must be enabled manually, using API or /setProtection command
AutoProtect: true
ServerProtect:
# If enabled and mojang server don't work, only registered players can join (So you must set "Registration" to true). If disabled, all players will be kicked.
Enabled: false
# If enabled plugin will remember if username was premium. (It's saved to database/file) NOTE: Enable that if you have bigger server, like 100/200+ players online.
Cache: true
# Server
# ignored by proxy plugin
Server:
# It will try check if cache is updated before player join.
UpdateCacheOnPreJoin: false
# It will try check if cache is updated after player join.
UpdateCacheOnJoin: true
# Proxy
# ignored by server
Proxy:
# Players needs to be logged in to use commands
NeedLoginToUseCommands: true
# Player can use this commands
NotBlocked:
- /login
- /register
- /l
# players from selected group can be send to other servers. This override Proxy/BungeeCord setting!
ForcedServers:
PREMIUM:
Enabled: false
# works like "default_server" from bungee
Server: PremiumServer
# works like "force_default_server" from bungee
Force: false
# works like "forced_hosts" from bungee -> this override "Force" option
ForcedHosts:
eg: nul.goto. ...\\.. .final..IIlIlIIlIl@71c66aab
# like "fallback_server" from bungee
Fallback: FallbackServer
CRACKED:
Enabled: true
# works like "default_server" from bungee
Server: CrackedServer
# works like "force_default_server" from bungee
Force: true
# like "fallback_server" from bungee
Fallback: FallbackServer
EXCEPTION:
Enabled: false
# works like "default_server" from bungee
Server: PremiumServer
# works like "force_default_server" from bungee
Force: true
# like "fallback_server" from bungee
Fallback: FallbackServer
# This status will use this same settings as parent status, but you can override that settings by adding them here.
Parent: CRACKED
ERROR:
Enabled: false
# works like "default_server" from bungee
Server: ErrorServer
# works like "force_default_server" from bungee
Force: false
# like "fallback_server" from bungee
Fallback: FallbackServer

149
pom.xml Normal file
View File

@ -0,0 +1,149 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.games647</groupId>
<!--This have to be in lowercase because it's used by plugin.yml-->
<artifactId>fastlogin</artifactId>
<packaging>jar</packaging>
<name>FastLogin</name>
<version>0.1</version>
<inceptionYear>2015</inceptionYear>
<url>http://dev.bukkit.org/bukkit-plugins/fastlogin</url>
<description>
Automatically logins premium player on a offline mode server
</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--Possibility to deploy directly to the plugins folder-->
<outputDir>${basedir}/target</outputDir>
</properties>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/games647/FastLogin/issues</url>
</issueManagement>
<scm>
<url>https://github.com/games647/FastLogin</url>
<connection>scm:git:git://github.com/games647/FastLogin.git</connection>
<developerConnection>scm:git:ssh://git@github.com:games647/FastLogin.git</developerConnection>
</scm>
<build>
<defaultGoal>install</defaultGoal>
<!--Just use the project name to replace an old version of the plugin if the user does only copy-paste-->
<finalName>${project.name}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<!--So many people still use Java 6 ;( http://mcstats.org/global/#Java+Version-->
<source>1.8</source>
<target>1.8</target>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
<!--false means actual true http://jira.codehaus.org/browse/MCOMPILER-209-->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<outputDirectory>${outputDir}</outputDirectory>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<!--Replace variables-->
<filtering>true</filtering>
</resource>
<!--Add the license to jar in order to see it in the final jar-->
<resource>
<directory>${basedir}</directory>
<includes>
<include>LICENSE</include>
</includes>
</resource>
</resources>
</build>
<repositories>
<!--Bukkit-Server-API -->
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<!--ProtocolLib-->
<repository>
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/content/groups/public/</url>
</repository>
<!--Authme Reloaded-->
<repository>
<id>xephi-repo</id>
<url>http://ci.xephi.fr/plugin/repository/everything/</url>
</repository>
<!--xAuth-->
<repository>
<id>luricos.de-repo</id>
<url>http://repo.luricos.de/bukkit-plugins/</url>
</repository>
</repositories>
<dependencies>
<!--Server API-->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!--Library for listening and sending Minecraft packets-->
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>3.6.3-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<!--Login Plugins-->
<dependency>
<groupId>fr.xephi</groupId>
<artifactId>authme</artifactId>
<version>5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>de.luricos.bukkit</groupId>
<artifactId>xAuth</artifactId>
<version>2.6</version>
<exclusions>
<exclusion>
<groupId>net.gravitydevelopment.updater</groupId>
<artifactId>updater</artifactId>
</exclusion>
<exclusion>
<groupId>net.ess3</groupId>
<artifactId>EssentialsGroupManager</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,93 @@
package com.github.games647.fastlogin;
import com.github.games647.fastlogin.listener.PlayerListener;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.github.games647.fastlogin.listener.EncryptionPacketListener;
import com.github.games647.fastlogin.listener.StartPacketListener;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
import org.bukkit.plugin.java.JavaPlugin;
public class FastLogin extends JavaPlugin {
private final KeyPair keyPair = generateKey();
private final Cache<String, PlayerData> session = CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.MINUTES)
.build();
@Override
public void onEnable() {
if (!isEnabled()) {
return;
}
if (!getServer().getPluginManager().isPluginEnabled("AuthMe")
&& !getServer().getPluginManager().isPluginEnabled("xAuth")) {
getLogger().warning("No support offline Auth plugin found. ");
getLogger().warning("Disabling this plugin...");
setEnabled(false);
return;
}
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
protocolManager.addPacketListener(new EncryptionPacketListener(this, protocolManager));
protocolManager.addPacketListener(new StartPacketListener(this, protocolManager));
getServer().getPluginManager().registerEvents(new PlayerListener(this), this);
}
@Override
public void onLoad() {
//online mode is only changeable aftter a restart
if (getServer().getOnlineMode()) {
getLogger().severe("Server have to be in offline mode");
setEnabled(false);
}
generateKey();
}
private KeyPair generateKey() {
try {
KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance("RSA");
keypairgenerator.initialize(1024);
return keypairgenerator.generateKeyPair();
} catch (NoSuchAlgorithmException noSuchAlgorithmException) {
//Should be default existing in every vm
}
return null;
}
public Cache<String, PlayerData> getSession() {
return session;
}
public KeyPair getKeyPair() {
return keyPair;
}
public HttpURLConnection getConnection(String url) throws IOException {
final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(15000);
connection.setReadTimeout(15000);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", "Premium-Checker");
return connection;
}
}

View File

@ -0,0 +1,20 @@
package com.github.games647.fastlogin;
public class PlayerData {
private final byte[] verifyToken;
private final String username;
public PlayerData(byte[] verifyToken, String username) {
this.username = username;
this.verifyToken = verifyToken;
}
public byte[] getVerifyToken() {
return verifyToken;
}
public String getUsername() {
return username;
}
}

View File

@ -0,0 +1,145 @@
package com.github.games647.fastlogin.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.server.SocketInjector;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.fastlogin.FastLogin;
import com.github.games647.fastlogin.PlayerData;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.security.PrivateKey;
import java.util.Arrays;
import java.util.logging.Level;
import javax.crypto.SecretKey;
import net.minecraft.server.v1_8_R3.MinecraftEncryption;
import net.minecraft.server.v1_8_R3.NetworkManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
public class EncryptionPacketListener extends PacketAdapter {
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?";
private final ProtocolManager protocolManager;
private final FastLogin fastLogin;
public EncryptionPacketListener(FastLogin plugin, ProtocolManager protocolManger) {
super(params(plugin, PacketType.Login.Client.ENCRYPTION_BEGIN).optionAsync());
this.fastLogin = plugin;
this.protocolManager = protocolManger;
}
/*
* C->S : Handshake State=2
* C->S : Login Start
* S->C : Encryption Key Request
* (Client Auth)
* C->S : Encryption Key Response
* (Server Auth, Both enable encryption)
* S->C : Login Success (*)
*/
@Override
public void onPacketReceiving(PacketEvent event) {
PacketContainer packet = event.getPacket();
Player player = event.getPlayer();
final byte[] sharedSecret = packet.getByteArrays().read(0);
byte[] clientVerify = packet.getByteArrays().read(1);
PrivateKey privateKey = fastLogin.getKeyPair().getPrivate();
String addressString = player.getAddress().toString();
PlayerData cachedEntry = fastLogin.getSession().asMap().get(addressString);
byte[] serverVerify = cachedEntry.getVerifyToken();
if (!Arrays.equals(serverVerify, MinecraftEncryption.b(privateKey, clientVerify))) {
player.kickPlayer("Invalid token");
event.setCancelled(true);
return;
}
//encrypt all following packets
NetworkManager networkManager = getNetworkManager(event);
SecretKey loginKey = MinecraftEncryption.a(privateKey, sharedSecret);
networkManager.a(loginKey);
String serverId = (new BigInteger(MinecraftEncryption.a("", fastLogin.getKeyPair().getPublic(), loginKey)))
.toString(16);
String username = cachedEntry.getUsername();
if (!hasJoinedServer(username, serverId)) {
//user tried to fake a authentification
player.kickPlayer("Invalid session");
event.setCancelled(true);
return;
}
//fake a new login packet
PacketContainer startPacket = protocolManager.createPacket(PacketType.Login.Client.START, true);
WrappedGameProfile fakeProfile = WrappedGameProfile.fromOfflinePlayer(Bukkit.getOfflinePlayer(username));
startPacket.getGameProfiles().write(0, fakeProfile);
try {
protocolManager.recieveClientPacket(event.getPlayer(), startPacket, false);
} catch (InvocationTargetException | IllegalAccessException ex) {
plugin.getLogger().log(Level.WARNING, null, ex);
}
event.setCancelled(true);
}
private NetworkManager getNetworkManager(PacketEvent event) throws IllegalArgumentException {
SocketInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(event.getPlayer());
NetworkManager networkManager = null;
try {
Field declaredField = injector.getClass().getDeclaredField("injector");
declaredField.setAccessible(true);
Object rawInjector = declaredField.get(injector);
declaredField = rawInjector.getClass().getDeclaredField("networkManager");
declaredField.setAccessible(true);
networkManager = (NetworkManager) declaredField.get(rawInjector);
} catch (IllegalAccessException | NoSuchFieldException ex) {
plugin.getLogger().log(Level.WARNING, null, ex);
}
return networkManager;
}
private boolean hasJoinedServer(String username, String serverId) {
try {
String url = HAS_JOINED_URL + "username=" + username + "&serverId=" + serverId;
HttpURLConnection conn = fastLogin.getConnection(url);
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = reader.readLine();
if (!line.equals("null")) {
JSONObject object = (JSONObject) JSONValue.parse(line);
String uuid = (String) object.get("id");
String name = (String) object.get("name");
return true;
}
} catch (IOException ex) {
plugin.getLogger().log(Level.WARNING, null, ex);
}
return false;
}
}

View File

@ -0,0 +1,59 @@
package com.github.games647.fastlogin.listener;
import com.github.games647.fastlogin.FastLogin;
import de.luricos.bukkit.xAuth.xAuth;
import de.luricos.bukkit.xAuth.xAuthPlayer;
import de.luricos.bukkit.xAuth.xAuthPlayer.Status;
import fr.xephi.authme.api.NewAPI;
import fr.xephi.authme.cache.limbo.LimboCache;
import java.sql.Timestamp;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
public class PlayerListener implements Listener {
private final FastLogin plugin;
public PlayerListener(FastLogin plugin) {
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true)
public void onJoin(PlayerJoinEvent joinEvent) {
final Player player = joinEvent.getPlayer();
String address = player.getAddress().toString();
if (plugin.getSession().asMap().containsKey(address)) {
Bukkit.getScheduler().runTaskLater(plugin, () -> {
doLogin(player);
}, 1 * 20L);
}
}
private void doLogin(Player player) {
if (Bukkit.getPluginManager().isPluginEnabled("AuthMe")) {
//add cache entry - otherwise loggin wouldn't work
LimboCache.getInstance().addLimboPlayer(player);
//skips registration and login
NewAPI.getInstance().forceLogin(player);
} else if (Bukkit.getPluginManager().isPluginEnabled("xAuth")) {
xAuth xAuthPlugin = xAuth.getPlugin();
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
xAuthPlayer.setPremium(true);
xAuthPlugin.getAuthClass(xAuthPlayer).online(xAuthPlayer.getName());
xAuthPlayer.setLoginTime(new Timestamp(System.currentTimeMillis()));
xAuthPlayer.setStatus(Status.AUTHENTICATED);
xAuthPlugin.getPlayerManager().unprotect(xAuthPlayer);
}
}
}

View File

@ -0,0 +1,90 @@
package com.github.games647.fastlogin.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.FastLogin;
import com.github.games647.fastlogin.PlayerData;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.HttpURLConnection;
import java.security.PublicKey;
import java.util.Random;
import java.util.logging.Level;
import org.bukkit.entity.Player;
public class StartPacketListener extends PacketAdapter {
//only premium members have a uuid from there
private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/";
private final ProtocolManager protocolManager;
private final FastLogin fastLogin;
private final Random random = new Random();
public StartPacketListener(FastLogin plugin, ProtocolManager protocolManger) {
super(params(plugin, PacketType.Login.Client.START).optionAsync());
this.fastLogin = plugin;
this.protocolManager = protocolManger;
}
/*
* C->S : Handshake State=2
* C->S : Login Start
* S->C : Encryption Key Request
* (Client Auth)
* C->S : Encryption Key Response
* (Server Auth, Both enable encryption)
* S->C : Login Success (*)
*/
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
PacketContainer packet = packetEvent.getPacket();
Player player = packetEvent.getPlayer();
String username = packet.getGameProfiles().read(0).getName();
if (isPremium(username)) {
//do premium login process
try {
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN, true);
//constr ServerID=""
//public key=plugin.getPublic
newPacket.getSpecificModifier(PublicKey.class).write(0, fastLogin.getKeyPair().getPublic());
byte[] verifyToken = new byte[4];
random.nextBytes(verifyToken);
newPacket.getByteArrays().write(0, verifyToken);
String addressString = player.getAddress().toString();
fastLogin.getSession().asMap().put(addressString, new PlayerData(verifyToken, username));
protocolManager.sendServerPacket(player, newPacket, false);
} catch (InvocationTargetException ex) {
plugin.getLogger().log(Level.SEVERE, null, ex);
}
//cancel only if the player is premium
packetEvent.setCancelled(true);
}
}
private boolean isPremium(String playerName) {
try {
final HttpURLConnection connection = fastLogin.getConnection(UUID_LINK + playerName);
final int responseCode = connection.getResponseCode();
return responseCode == HttpURLConnection.HTTP_OK;
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, null, ex);
}
return false;
}
}

View File

@ -0,0 +1,14 @@
# project informations for Bukkit in order to register our plugin with all it components
# ${project.name} are variables from Maven (pom.xml) which will be replaced after the build
name: ${project.name}
version: ${project.version}
main: ${project.groupId}.${project.artifactId}.${project.name}
# meta informations for plugin managers
authors: [Xeroun, games647, 'https://github.com/games647/FastLogin/graphs/contributors']
description: |
${project.description}
website: ${project.url}
dev-url: ${project.url}
depend: [ProtocolLib]