mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 09:26:11 -04:00
proper mojang api
This commit is contained in:
parent
55231a5a65
commit
b8a0e74434
@ -15,4 +15,5 @@ package de.bixilon.minosoft;
|
|||||||
|
|
||||||
public class Config {
|
public class Config {
|
||||||
public static String homeDir;
|
public static String homeDir;
|
||||||
|
public static String configFileName = "game.yml";
|
||||||
}
|
}
|
||||||
|
@ -15,18 +15,21 @@ package de.bixilon.minosoft;
|
|||||||
|
|
||||||
import de.bixilon.minosoft.config.Configuration;
|
import de.bixilon.minosoft.config.Configuration;
|
||||||
import de.bixilon.minosoft.config.GameConfiguration;
|
import de.bixilon.minosoft.config.GameConfiguration;
|
||||||
|
import de.bixilon.minosoft.game.datatypes.Player;
|
||||||
import de.bixilon.minosoft.logging.Log;
|
import de.bixilon.minosoft.logging.Log;
|
||||||
import de.bixilon.minosoft.logging.LogLevel;
|
import de.bixilon.minosoft.logging.LogLevel;
|
||||||
import de.bixilon.minosoft.objects.Account;
|
import de.bixilon.minosoft.mojang.api.MojangAccount;
|
||||||
import de.bixilon.minosoft.objects.Player;
|
|
||||||
import de.bixilon.minosoft.protocol.network.Connection;
|
import de.bixilon.minosoft.protocol.network.Connection;
|
||||||
import de.bixilon.minosoft.util.OSUtil;
|
import de.bixilon.minosoft.util.OSUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class Minosoft {
|
public class Minosoft {
|
||||||
static Configuration config;
|
static Configuration config;
|
||||||
|
static List<MojangAccount> accountList;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// int log thread
|
// int log thread
|
||||||
@ -36,7 +39,7 @@ public class Minosoft {
|
|||||||
setConfigFolder();
|
setConfigFolder();
|
||||||
Log.info("Reading config file...");
|
Log.info("Reading config file...");
|
||||||
try {
|
try {
|
||||||
config = new Configuration("game.yml");
|
config = new Configuration(Config.configFileName);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.fatal("Failed to load config file!");
|
Log.fatal("Failed to load config file!");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -46,8 +49,21 @@ public class Minosoft {
|
|||||||
// set log level from config
|
// set log level from config
|
||||||
Log.setLevel(LogLevel.byName(config.getString(GameConfiguration.GENERAL_LOG_LEVEL)));
|
Log.setLevel(LogLevel.byName(config.getString(GameConfiguration.GENERAL_LOG_LEVEL)));
|
||||||
|
|
||||||
|
checkClientToken();
|
||||||
|
|
||||||
Connection c = new Connection(config.getString("debug.host"), config.getInteger("debug.port"));
|
Connection c = new Connection(config.getString("debug.host"), config.getInteger("debug.port"));
|
||||||
c.setPlayer(new Player(new Account(config.getString("debug.username"), config.getString("debug.password"))));
|
accountList = config.getMojangAccounts();
|
||||||
|
if (accountList.size() == 0) {
|
||||||
|
throw new RuntimeException("No accounts in config file!");
|
||||||
|
//MojangAccount account = MojangAuthentication.login("email", "password");
|
||||||
|
}
|
||||||
|
MojangAccount account = accountList.get(0);
|
||||||
|
if (!account.refreshToken()) {
|
||||||
|
// could not login
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
account.saveToConfig();
|
||||||
|
c.setPlayer(new Player(account));
|
||||||
c.connect();
|
c.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,4 +95,11 @@ public class Minosoft {
|
|||||||
public static Configuration getConfig() {
|
public static Configuration getConfig() {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void checkClientToken() {
|
||||||
|
if (config.getString(GameConfiguration.CLIENT_TOKEN) == null || config.getString(GameConfiguration.CLIENT_TOKEN).equals("randomGenerated")) {
|
||||||
|
config.putString(GameConfiguration.CLIENT_TOKEN, UUID.randomUUID().toString());
|
||||||
|
config.saveToFile(Config.configFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.bixilon.minosoft.objects;
|
package de.bixilon.minosoft;
|
||||||
|
|
||||||
import de.bixilon.minosoft.game.datatypes.TextComponent;
|
import de.bixilon.minosoft.game.datatypes.TextComponent;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
@ -14,12 +14,13 @@
|
|||||||
package de.bixilon.minosoft.config;
|
package de.bixilon.minosoft.config;
|
||||||
|
|
||||||
import de.bixilon.minosoft.Config;
|
import de.bixilon.minosoft.Config;
|
||||||
|
import de.bixilon.minosoft.mojang.api.MojangAccount;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.*;
|
||||||
|
|
||||||
public class Configuration {
|
public class Configuration {
|
||||||
LinkedHashMap<String, Object> config;
|
LinkedHashMap<String, Object> config;
|
||||||
@ -69,6 +70,38 @@ public class Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void putBoolean(ConfigEnum config, boolean value) {
|
||||||
|
putBoolean(config.getPath(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBoolean(String path, boolean value) {
|
||||||
|
put(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInteger(ConfigEnum config, int value) {
|
||||||
|
putInteger(config.getPath(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInteger(String path, int value) {
|
||||||
|
put(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putString(ConfigEnum config, String value) {
|
||||||
|
putString(config.getPath(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putString(String path, String value) {
|
||||||
|
put(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putMojangAccount(MojangAccount account) {
|
||||||
|
String basePath = String.format("account.accounts.%s.", account.getUserId());
|
||||||
|
putString(basePath + "accessToken", account.getAccessToken());
|
||||||
|
putString(basePath + "uuid", account.getUUID().toString());
|
||||||
|
putString(basePath + "userName", account.getMojangUserName());
|
||||||
|
putString(basePath + "playerName", account.getPlayerName());
|
||||||
|
}
|
||||||
|
|
||||||
public Object get(String path) {
|
public Object get(String path) {
|
||||||
if (path.contains(".")) {
|
if (path.contains(".")) {
|
||||||
// split
|
// split
|
||||||
@ -78,9 +111,54 @@ public class Configuration {
|
|||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
temp = (LinkedHashMap<String, Object>) temp.get(spilt[i]);
|
temp = (LinkedHashMap<String, Object>) temp.get(spilt[i]);
|
||||||
}
|
}
|
||||||
|
if (temp == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return temp.get(spilt[spilt.length - 1]);
|
return temp.get(spilt[spilt.length - 1]);
|
||||||
}
|
}
|
||||||
return config.get(path);
|
return config.get(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void put(String path, Serializable value) {
|
||||||
|
if (path.contains(".")) {
|
||||||
|
// split
|
||||||
|
String[] spilt = path.split("\\.");
|
||||||
|
LinkedHashMap<String, Object> temp = config;
|
||||||
|
for (int i = 0; i < spilt.length - 1; i++) {
|
||||||
|
if (temp.get(spilt[i]) == null) {
|
||||||
|
// not yet existing, creating it
|
||||||
|
temp.put(spilt[i], new LinkedHashMap<String, Object>());
|
||||||
|
}
|
||||||
|
temp = (LinkedHashMap<String, Object>) temp.get(spilt[i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
temp.put(spilt[spilt.length - 1], value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config.put(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveToFile(String filename) {
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
FileWriter writer = null;
|
||||||
|
try {
|
||||||
|
writer = new FileWriter(Config.homeDir + "config/" + filename);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
yaml.dump(config, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MojangAccount> getMojangAccounts() {
|
||||||
|
List<MojangAccount> accounts = new ArrayList<>();
|
||||||
|
LinkedHashMap<String, Object> objects = (LinkedHashMap<String, Object>) get("account.accounts");
|
||||||
|
for (Map.Entry<String, Object> set : objects.entrySet()) {
|
||||||
|
LinkedHashMap<String, Object> entry = (LinkedHashMap<String, Object>) set.getValue();
|
||||||
|
accounts.add(new MojangAccount((String) entry.get("accessToken"), set.getKey(), UUID.fromString((String) entry.get("uuid")), (String) entry.get("playerName"), (String) entry.get("mojangUserName")));
|
||||||
|
}
|
||||||
|
return accounts;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ public enum GameConfiguration implements ConfigEnum {
|
|||||||
CONFIG_VERSION("version"),
|
CONFIG_VERSION("version"),
|
||||||
GAME_RENDER_DISTANCE("game.render-distance"),
|
GAME_RENDER_DISTANCE("game.render-distance"),
|
||||||
NETWORK_FAKE_CLIENT_BRAND("network.fake-client-brand"),
|
NETWORK_FAKE_CLIENT_BRAND("network.fake-client-brand"),
|
||||||
GENERAL_LOG_LEVEL("general.log-level");
|
GENERAL_LOG_LEVEL("general.log-level"),
|
||||||
|
CLIENT_TOKEN("account.clientToken");
|
||||||
|
|
||||||
|
|
||||||
final String path;
|
final String path;
|
||||||
|
@ -11,20 +11,18 @@
|
|||||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.bixilon.minosoft.objects;
|
package de.bixilon.minosoft.game.datatypes;
|
||||||
|
|
||||||
import de.bixilon.minosoft.game.datatypes.GameMode;
|
|
||||||
import de.bixilon.minosoft.game.datatypes.Slot;
|
|
||||||
import de.bixilon.minosoft.game.datatypes.Slots;
|
|
||||||
import de.bixilon.minosoft.game.datatypes.entities.Location;
|
import de.bixilon.minosoft.game.datatypes.entities.Location;
|
||||||
import de.bixilon.minosoft.game.datatypes.entities.meta.HumanMetaData;
|
import de.bixilon.minosoft.game.datatypes.entities.meta.HumanMetaData;
|
||||||
import de.bixilon.minosoft.game.datatypes.world.World;
|
import de.bixilon.minosoft.game.datatypes.world.World;
|
||||||
|
import de.bixilon.minosoft.mojang.api.MojangAccount;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class Player {
|
public class Player {
|
||||||
final Account acc;
|
final MojangAccount acc;
|
||||||
float health;
|
float health;
|
||||||
short food;
|
short food;
|
||||||
float saturation;
|
float saturation;
|
||||||
@ -39,9 +37,8 @@ public class Player {
|
|||||||
HashMap<Slots.Inventory, Slot> inventory = new HashMap<>();
|
HashMap<Slots.Inventory, Slot> inventory = new HashMap<>();
|
||||||
boolean spawnConfirmed = false;
|
boolean spawnConfirmed = false;
|
||||||
|
|
||||||
public Player(Account acc) {
|
public Player(MojangAccount acc) {
|
||||||
this.acc = acc;
|
this.acc = acc;
|
||||||
acc.login();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPlayerName() {
|
public String getPlayerName() {
|
||||||
@ -52,7 +49,7 @@ public class Player {
|
|||||||
return acc.getUUID();
|
return acc.getUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getAccount() {
|
public MojangAccount getAccount() {
|
||||||
return this.acc;
|
return this.acc;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Codename Minosoft
|
||||||
|
* Copyright (C) 2020 Moritz Zwerger
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.bixilon.minosoft.mojang.api;
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.Config;
|
||||||
|
import de.bixilon.minosoft.Minosoft;
|
||||||
|
import de.bixilon.minosoft.util.Util;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class MojangAccount {
|
||||||
|
|
||||||
|
String accessToken;
|
||||||
|
String userId;
|
||||||
|
|
||||||
|
UUID uuid;
|
||||||
|
String playerName;
|
||||||
|
|
||||||
|
String mojangUserName;
|
||||||
|
|
||||||
|
public MojangAccount(JSONObject json) {
|
||||||
|
this.accessToken = json.getString("accessToken");
|
||||||
|
JSONObject profile = json.getJSONObject("selectedProfile");
|
||||||
|
this.uuid = Util.formatUUID(profile.getString("id"));
|
||||||
|
this.playerName = profile.getString("name");
|
||||||
|
|
||||||
|
JSONObject mojang = json.getJSONObject("user");
|
||||||
|
this.userId = mojang.getString("id");
|
||||||
|
this.mojangUserName = mojang.getString("username");
|
||||||
|
}
|
||||||
|
|
||||||
|
public MojangAccount(String accessToken, String userId, UUID uuid, String playerName, String mojangUserName) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.userId = userId;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.playerName = playerName;
|
||||||
|
this.mojangUserName = mojangUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void join(String serverId) {
|
||||||
|
MojangAuthentication.joinServer(this, serverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean refreshToken() {
|
||||||
|
String accessToken = MojangAuthentication.refresh(this.accessToken);
|
||||||
|
if (accessToken == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getUUID() {
|
||||||
|
return this.uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlayerName() {
|
||||||
|
return this.playerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMojangUserName() {
|
||||||
|
return mojangUserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveToConfig() {
|
||||||
|
Minosoft.getConfig().putMojangAccount(this);
|
||||||
|
Minosoft.getConfig().saveToFile(Config.configFileName);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Codename Minosoft
|
||||||
|
* Copyright (C) 2020 Moritz Zwerger
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.bixilon.minosoft.mojang.api;
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.Minosoft;
|
||||||
|
import de.bixilon.minosoft.config.GameConfiguration;
|
||||||
|
import de.bixilon.minosoft.logging.Log;
|
||||||
|
import de.bixilon.minosoft.util.HTTP;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
|
||||||
|
public class MojangAuthentication {
|
||||||
|
|
||||||
|
public static MojangAccount login(String clientToken, String username, String password) {
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("agent", new JSONObject().put("name", "Minecraft").put("version", 1));
|
||||||
|
payload.put("username", username);
|
||||||
|
payload.put("password", password);
|
||||||
|
payload.put("clientToken", clientToken);
|
||||||
|
payload.put("requestUser", true);
|
||||||
|
|
||||||
|
HttpResponse<String> response = HTTP.postJson(MojangURLs.LOGIN.getUrl(), payload);
|
||||||
|
if (response == null) {
|
||||||
|
Log.mojang(String.format("Failed to login with username %s", username));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject jsonResponse = new JSONObject(response.body());
|
||||||
|
if (response.statusCode() != 200) {
|
||||||
|
Log.mojang(String.format("Failed to login with error code %d: %s", response.statusCode(), jsonResponse.getString("errorMessage")));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// now it is okay
|
||||||
|
return new MojangAccount(jsonResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MojangAccount login(String username, String password) {
|
||||||
|
return login(Minosoft.getConfig().getString(GameConfiguration.CLIENT_TOKEN), username, password);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void joinServer(MojangAccount account, String serverId) {
|
||||||
|
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("accessToken", account.getAccessToken());
|
||||||
|
payload.put("selectedProfile", account.getUUID().toString().replace("-", ""));
|
||||||
|
payload.put("serverId", serverId);
|
||||||
|
|
||||||
|
HttpResponse<String> response = HTTP.postJson(MojangURLs.JOIN.toString(), payload);
|
||||||
|
|
||||||
|
if (response == null) {
|
||||||
|
Log.mojang(String.format("Failed to join server: %s", serverId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JSONObject jsonResponse = new JSONObject(response.body());
|
||||||
|
if (response.statusCode() != 204) {
|
||||||
|
Log.mojang(String.format("Failed to join server with error code %d: %s", response.statusCode(), jsonResponse.getString("errorMessage")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// joined
|
||||||
|
Log.mojang("Joined server successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String refresh(String clientToken, String accessToken) {
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("accessToken", accessToken);
|
||||||
|
payload.put("clientToken", clientToken);
|
||||||
|
|
||||||
|
HttpResponse<String> response = HTTP.postJson(MojangURLs.REFRESH.getUrl(), payload);
|
||||||
|
if (response == null) {
|
||||||
|
Log.mojang("Failed to refresh session");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject jsonResponse = new JSONObject(response.body());
|
||||||
|
if (response.statusCode() != 200) {
|
||||||
|
Log.mojang(String.format("Failed to refresh session with error code %d: %s", response.statusCode(), jsonResponse.getString("errorMessage")));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// now it is okay
|
||||||
|
return jsonResponse.getString("accessToken");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String refresh(String accessToken) {
|
||||||
|
return refresh(Minosoft.getConfig().getString(GameConfiguration.CLIENT_TOKEN), accessToken);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Codename Minosoft
|
||||||
|
* Copyright (C) 2020 Moritz Zwerger
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.bixilon.minosoft.mojang.api;
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.logging.Log;
|
||||||
|
import de.bixilon.minosoft.util.HTTP;
|
||||||
|
import de.bixilon.minosoft.util.Util;
|
||||||
|
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MojangBlockedServers {
|
||||||
|
|
||||||
|
|
||||||
|
public static List<String> getBlockedServers() {
|
||||||
|
HttpResponse<String> response = HTTP.get(MojangURLs.BLOCKED_SERVERS.getUrl());
|
||||||
|
if (response == null) {
|
||||||
|
Log.mojang("Failed to fetch blocked servers");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (response.statusCode() != 200) {
|
||||||
|
Log.mojang(String.format("Failed to fetch blocked server error code %d", response.statusCode()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// now it is hopefully okay
|
||||||
|
return new ArrayList<>(Arrays.asList(response.body().split("\n")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isServerBlocked(List<String> list, String hostname) {
|
||||||
|
for (String hash : list) {
|
||||||
|
if (hash.equals(Util.sha1(hostname))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// ToDo: check subdomains and ip addresses
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
115
src/main/java/de/bixilon/minosoft/mojang/api/MojangStatus.java
Normal file
115
src/main/java/de/bixilon/minosoft/mojang/api/MojangStatus.java
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Codename Minosoft
|
||||||
|
* Copyright (C) 2020 Moritz Zwerger
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.bixilon.minosoft.mojang.api;
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.logging.Log;
|
||||||
|
import de.bixilon.minosoft.util.HTTP;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
public class MojangStatus {
|
||||||
|
public static HashMap<Services, ServiceStatus> getStatus() {
|
||||||
|
HttpResponse<String> response = HTTP.get(MojangURLs.STATUS.getUrl());
|
||||||
|
if (response == null) {
|
||||||
|
Log.mojang("Failed to fetch Status");
|
||||||
|
return getUnknownStatusMap();
|
||||||
|
}
|
||||||
|
if (response.statusCode() != 200) {
|
||||||
|
Log.mojang(String.format("Failed to fetch Status with error code %d", response.statusCode()));
|
||||||
|
return getUnknownStatusMap();
|
||||||
|
}
|
||||||
|
// now it is hopefully okay
|
||||||
|
HashMap<Services, ServiceStatus> ret = new HashMap<>();
|
||||||
|
try {
|
||||||
|
JSONArray json = new JSONArray(response.body());
|
||||||
|
for (int i = 0; i < json.length(); i++) {
|
||||||
|
JSONObject innerJson = json.getJSONObject(i);
|
||||||
|
Iterator<String> keys = innerJson.keys();
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
String key = keys.next();
|
||||||
|
Services service = Services.byKey(key);
|
||||||
|
ret.put(service, ServiceStatus.byKey(innerJson.getString(key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret.size() != Services.values().length) {
|
||||||
|
// new service or old one removed, technically an error
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
} catch (NullPointerException | JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return getUnknownStatusMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HashMap<Services, ServiceStatus> getUnknownStatusMap() {
|
||||||
|
HashMap<Services, ServiceStatus> ret = new HashMap<>();
|
||||||
|
for (Services service : Services.values()) {
|
||||||
|
ret.put(service, ServiceStatus.UNKNOWN);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Services {
|
||||||
|
MINECRAFT_NET("minecraft.net"),
|
||||||
|
ACCOUNT("account.mojang.com"),
|
||||||
|
AUTH("auth.mojang.com"),
|
||||||
|
AUTHENTICATION_SERVER("authserver.mojang.com"),
|
||||||
|
SESSION_SERVER("sessionserver.mojang.com"),
|
||||||
|
API("api.mojang.com"),
|
||||||
|
TEXTURES("textures.minecraft.net"),
|
||||||
|
MOJANG_COM("mojang.com");
|
||||||
|
|
||||||
|
final String key;
|
||||||
|
|
||||||
|
Services(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Services byKey(String key) {
|
||||||
|
for (Services s : values()) {
|
||||||
|
if (s.getKey().equals(key)) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ServiceStatus {
|
||||||
|
GREEN,
|
||||||
|
YELLOW,
|
||||||
|
RED,
|
||||||
|
UNKNOWN;
|
||||||
|
|
||||||
|
public static ServiceStatus byKey(String key) {
|
||||||
|
for (ServiceStatus s : values()) {
|
||||||
|
if (s.name().equals(key)) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
src/main/java/de/bixilon/minosoft/mojang/api/MojangURLs.java
Normal file
46
src/main/java/de/bixilon/minosoft/mojang/api/MojangURLs.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Codename Minosoft
|
||||||
|
* Copyright (C) 2020 Moritz Zwerger
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.bixilon.minosoft.mojang.api;
|
||||||
|
|
||||||
|
public enum MojangURLs {
|
||||||
|
STATUS("https://status.mojang.com/check"),
|
||||||
|
BLOCKED_SERVERS("https://sessionserver.mojang.com/blockedservers"),
|
||||||
|
LOGIN("https://authserver.mojang.com/authenticate"),
|
||||||
|
JOIN("https://sessionserver.mojang.com/session/minecraft/join"),
|
||||||
|
REFRESH("https://authserver.mojang.com/refresh");
|
||||||
|
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
MojangURLs(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MojangURLs byUrl(String key) {
|
||||||
|
for (MojangURLs s : values()) {
|
||||||
|
if (s.getUrl().equals(key)) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Codename Minosoft
|
|
||||||
* Copyright (C) 2020 Moritz Zwerger
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.bixilon.minosoft.objects;
|
|
||||||
|
|
||||||
import de.bixilon.minosoft.logging.Log;
|
|
||||||
import de.bixilon.minosoft.util.HTTP;
|
|
||||||
import de.bixilon.minosoft.util.Util;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class Account {
|
|
||||||
|
|
||||||
final String username;
|
|
||||||
final String password;
|
|
||||||
String playerName;
|
|
||||||
|
|
||||||
String token;
|
|
||||||
UUID uuid;
|
|
||||||
|
|
||||||
public Account(String username, String password) {
|
|
||||||
this.username = username;
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void login() {
|
|
||||||
JSONObject payload = new JSONObject();
|
|
||||||
payload.put("agent", new JSONObject().put("name", "Minecraft").put("version", 1));
|
|
||||||
payload.put("username", username);
|
|
||||||
payload.put("password", password);
|
|
||||||
// ToDo not in main thread
|
|
||||||
HttpResponse<String> response = HTTP.postJson("https://authserver.mojang.com/authenticate", payload);
|
|
||||||
if (response == null || response.statusCode() != 200) {
|
|
||||||
assert response != null;
|
|
||||||
Log.info(String.format("[Mojang API] Login failed with username=%s (%s)", username, response.statusCode()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Log.info(String.format("[Mojang API] Login successful with username=%s", username));
|
|
||||||
|
|
||||||
// login good
|
|
||||||
JSONObject raw = new JSONObject(response.body());
|
|
||||||
|
|
||||||
token = raw.getString("accessToken");
|
|
||||||
|
|
||||||
uuid = Util.formatUUID(raw.getJSONObject("selectedProfile").getString("id"));
|
|
||||||
playerName = raw.getJSONObject("selectedProfile").getString("name");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void join(String serverId) {
|
|
||||||
JSONObject payload = new JSONObject();
|
|
||||||
payload.put("accessToken", token);
|
|
||||||
payload.put("selectedProfile", getUUID().toString().replace("-", ""));
|
|
||||||
payload.put("serverId", serverId);
|
|
||||||
// ToDo not in main thread
|
|
||||||
HttpResponse<String> response = HTTP.postJson("https://sessionserver.mojang.com/session/minecraft/join", payload);
|
|
||||||
if (response == null || response.statusCode() != 204) {
|
|
||||||
assert response != null;
|
|
||||||
Log.info("[Mojang API] Login to join server!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.info("[Mojang API] Joined server successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getUUID() {
|
|
||||||
return this.uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPlayerName() {
|
|
||||||
return this.playerName;
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.protocol.network;
|
package de.bixilon.minosoft.protocol.network;
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.game.datatypes.Player;
|
||||||
import de.bixilon.minosoft.logging.Log;
|
import de.bixilon.minosoft.logging.Log;
|
||||||
import de.bixilon.minosoft.objects.Player;
|
|
||||||
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
|
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
|
||||||
import de.bixilon.minosoft.protocol.packets.ServerboundPacket;
|
import de.bixilon.minosoft.protocol.packets.ServerboundPacket;
|
||||||
import de.bixilon.minosoft.protocol.packets.serverbound.handshaking.PacketHandshake;
|
import de.bixilon.minosoft.protocol.packets.serverbound.handshaking.PacketHandshake;
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.protocol.packets.clientbound.status;
|
package de.bixilon.minosoft.protocol.packets.clientbound.status;
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.ServerListPing;
|
||||||
import de.bixilon.minosoft.logging.Log;
|
import de.bixilon.minosoft.logging.Log;
|
||||||
import de.bixilon.minosoft.objects.ServerListPing;
|
|
||||||
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
|
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
|
||||||
import de.bixilon.minosoft.protocol.protocol.InPacketBuffer;
|
import de.bixilon.minosoft.protocol.protocol.InPacketBuffer;
|
||||||
import de.bixilon.minosoft.protocol.protocol.PacketHandler;
|
import de.bixilon.minosoft.protocol.protocol.PacketHandler;
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.protocol.packets.serverbound.login;
|
package de.bixilon.minosoft.protocol.packets.serverbound.login;
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.game.datatypes.Player;
|
||||||
import de.bixilon.minosoft.logging.Log;
|
import de.bixilon.minosoft.logging.Log;
|
||||||
import de.bixilon.minosoft.objects.Player;
|
|
||||||
import de.bixilon.minosoft.protocol.packets.ServerboundPacket;
|
import de.bixilon.minosoft.protocol.packets.ServerboundPacket;
|
||||||
import de.bixilon.minosoft.protocol.protocol.OutPacketBuffer;
|
import de.bixilon.minosoft.protocol.protocol.OutPacketBuffer;
|
||||||
import de.bixilon.minosoft.protocol.protocol.Packets;
|
import de.bixilon.minosoft.protocol.protocol.Packets;
|
||||||
|
@ -31,8 +31,20 @@ public class HTTP {
|
|||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.build();
|
.build();
|
||||||
try {
|
try {
|
||||||
return client.send(request,
|
return client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
HttpResponse.BodyHandlers.ofString());
|
} catch (IOException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HttpResponse<String> get(String url) {
|
||||||
|
HttpClient client = HttpClient.newHttpClient();
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(url))
|
||||||
|
.build();
|
||||||
|
try {
|
||||||
|
return client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@ import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.DataFormatException;
|
import java.util.zip.DataFormatException;
|
||||||
@ -77,4 +80,17 @@ public class Util {
|
|||||||
}
|
}
|
||||||
return outputStream.toByteArray();
|
return outputStream.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String sha1(String string) {
|
||||||
|
try {
|
||||||
|
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
|
||||||
|
crypt.reset();
|
||||||
|
crypt.update(string.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return new String(crypt.digest());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,11 @@ game:
|
|||||||
network:
|
network:
|
||||||
fake-client-brand: false # if true, minosoft will tell the server that this is unmodified vanilla. if false, it will tell "minosoft".
|
fake-client-brand: false # if true, minosoft will tell the server that this is unmodified vanilla. if false, it will tell "minosoft".
|
||||||
|
|
||||||
# this will be removed soon
|
account:
|
||||||
|
clientToken: "randomGenerated"
|
||||||
|
accounts:
|
||||||
|
|
||||||
|
# this will be removed soon, only for debugging (pre alpha stage): some features are not implemented yet -/-
|
||||||
debug:
|
debug:
|
||||||
host: "127.0.0.1"
|
host: "127.0.0.1"
|
||||||
port: 25565
|
port: 25565
|
||||||
username: "Player"
|
|
||||||
password: "secret123"
|
|
Loading…
x
Reference in New Issue
Block a user