Commit a5720973 authored by Magix's avatar Magix Committed by GitHub
Browse files

Merge branch 'development' into main

parents 708ee021 16318b37
package emu.grasscutter.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.GenshinPlayer;
import java.util.*;
/**
* A container for server-related commands.
*/
public final class ServerCommands {
@Command(label = "reload", usage = "reload", description = "Reload server config", permission = "server.reload")
public static class ReloadCommand implements CommandHandler {
@Override
public void execute(List<String> args) {
Grasscutter.getLogger().info("Reloading config.");
Grasscutter.loadConfig();
Grasscutter.getDispatchServer().loadQueries();
Grasscutter.getLogger().info("Reload complete.");
}
@Override
public void execute(GenshinPlayer player, List<String> args) {
CommandHandler.sendMessage(player, "Reloading config.");
this.execute(args);
CommandHandler.sendMessage(player, "Reload complete.");
}
}
@Command(label = "kick", usage = "kick <player>", description = "Kicks the specified player from the server (WIP)", permission = "server.kick")
public static class KickCommand implements CommandHandler {
@Override
public void execute(List<String> args) {
this.execute(null, args);
}
@Override
public void execute(GenshinPlayer player, List<String> args) {
int target = Integer.parseInt(args.get(0));
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
if(targetPlayer == null) {
CommandHandler.sendMessage(player, "Player not found.");
return;
}
if(player != null) {
CommandHandler.sendMessage(null, String.format("Player [%s:%s] has kicked player [%s:%s]", player.getAccount().getPlayerId(), player.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername()));
}
CommandHandler.sendMessage(player, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername()));
targetPlayer.getSession().close();
}
}
@Command(label = "stop", usage = "stop", description = "Stops the server", permission = "server.stop")
public static class StopCommand implements CommandHandler {
@Override
public void execute(List<String> args) {
Grasscutter.getLogger().info("Server shutting down...");
for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) {
p.dropMessage("Server shutting down...");
}
System.exit(1);
}
}
@Command(label = "broadcast", aliases = {"b"},
usage = "broadcast <message>", description = "Sends a message to all the players", permission = "server.broadcast")
public static class BroadcastCommand implements CommandHandler {
@Override
public void execute(List<String> args) {
if(args.size() < 1) {
CommandHandler.sendMessage(null, "Usage: broadcast <message>"); return;
}
String message = String.join(" ", args.subList(0, args.size()));
for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) {
p.dropMessage(message);
}
CommandHandler.sendMessage(null, "Message sent.");
}
@Override
public void execute(GenshinPlayer player, List<String> args) {
if(args.size() < 1) {
CommandHandler.sendMessage(player, "Usage: broadcast <message>"); return;
}
String message = String.join(" ", args.subList(0, args.size()));
for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) {
p.dropMessage(message);
}
CommandHandler.sendMessage(player, "Message sent.");
}
}
@Command(label = "sendmessage", aliases = {"sendmsg", "msg"},
usage = "sendmessage <player> <message>", description = "Sends a message to a player")
public static class SendMessageCommand implements CommandHandler {
@Override
public void execute(List<String> args) {
if(args.size() < 2) {
CommandHandler.sendMessage(null, "Usage: sendmessage <player> <message>"); return;
}
try {
int target = Integer.parseInt(args.get(0));
String message = String.join(" ", args.subList(1, args.size()));
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
if(targetPlayer == null) {
CommandHandler.sendMessage(null, "Player not found."); return;
}
targetPlayer.dropMessage(message);
CommandHandler.sendMessage(null, "Message sent.");
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(null, "Invalid player ID.");
}
}
@Override
public void execute(GenshinPlayer player, List<String> args) {
if(args.size() < 2) {
CommandHandler.sendMessage(player, "Usage: sendmessage <player> <message>"); return;
}
try {
int target = Integer.parseInt(args.get(0));
String message = String.join(" ", args.subList(1, args.size()));
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
if(targetPlayer == null) {
CommandHandler.sendMessage(player, "Player not found."); return;
}
targetPlayer.sendMessage(player, message);
CommandHandler.sendMessage(player, "Message sent.");
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(player, "Invalid player ID.");
}
}
}
@Command(label = "account",
usage = "account <create|delete> <username> [uid]",
description = "Modify user accounts", execution = Command.Execution.CONSOLE)
public static class AccountCommand implements CommandHandler {
@Override
public void execute(List<String> args) {
if(args.size() < 2) {
CommandHandler.sendMessage(null, "Usage: account <create|delete> <username> [uid]"); return;
}
String action = args.get(0);
String username = args.get(1);
switch(action) {
default:
CommandHandler.sendMessage(null, "Usage: account <create|delete> <username> [uid]");
return;
case "create":
int uid = 0;
if(args.size() > 2) {
try {
uid = Integer.parseInt(args.get(2));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(null, "Invalid UID."); return;
}
}
Account account = DatabaseHelper.createAccountWithId(username, uid);
if(account == null) {
CommandHandler.sendMessage(null, "Account already exists."); return;
} else {
CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + ".");
account.addPermission("*"); // Grant the player superuser permissions.
account.save(); // Save account to database.
}
return;
case "delete":
if(DatabaseHelper.deleteAccount(username)) {
CommandHandler.sendMessage(null, "Account deleted."); return;
} else CommandHandler.sendMessage(null, "Account not found.");
return;
}
}
}
@Command(label = "permission",
usage = "permission <add|remove> <username> <permission>",
description = "Grants or removes a permission for a user", permission = "*")
public static class PermissionCommand implements CommandHandler {
@Override
public void execute(List<String> args) {
if(args.size() < 3) {
CommandHandler.sendMessage(null, "Usage: permission <add|remove> <username> <permission>"); return;
}
String action = args.get(0);
String username = args.get(1);
String permission = args.get(2);
Account account = Grasscutter.getGameServer().getAccountByName(username);
if(account == null) {
CommandHandler.sendMessage(null, "Account not found."); return;
}
switch(action) {
default:
CommandHandler.sendMessage(null, "Usage: permission <add|remove> <username> <permission>");
break;
case "add":
if(account.addPermission(permission)) {
CommandHandler.sendMessage(null, "Permission added.");
} else CommandHandler.sendMessage(null, "They already have this permission!");
break;
case "remove":
if(account.removePermission(permission)) {
CommandHandler.sendMessage(null, "Permission removed.");
} else CommandHandler.sendMessage(null, "They don't have this permission!");
break;
}
account.save();
}
}
@Command(label = "help",
usage = "help [command]", description = "Sends the help message or shows information about a specified command")
public static class HelpCommand implements CommandHandler {
@Override
public void execute(List<String> args) {
this.execute(null, args);
}
@Override
public void execute(GenshinPlayer player, List<String> args) {
if(args.size() < 1) {
HashMap<String, CommandHandler> handlers = CommandMap.getInstance().getHandlers();
List<Command> annotations = new ArrayList<>();
for(String key : handlers.keySet()) {
Command annotation = handlers.get(key).getClass().getAnnotation(Command.class);
if(!Arrays.asList(annotation.aliases()).contains(key)) {
if(player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) continue;
annotations.add(annotation);
}
}
SendAllHelpMessage(player, annotations);
} else {
String command = args.get(0);
CommandHandler handler = CommandMap.getInstance().getHandler(command);
StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n");
if(handler == null) {
builder.append("No command found.");
} else {
Command annotation = handler.getClass().getAnnotation(Command.class);
builder.append(" ").append(annotation.description()).append("\n");
builder.append(" Usage: ").append(annotation.usage());
if(annotation.aliases().length >= 1) {
builder.append("\n").append(" Aliases: ");
for (String alias : annotation.aliases()) {
builder.append(alias).append(" ");
}
}
if(player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) {
builder.append("\n Warning: You do not have permission to run this command.");
}
}
CommandHandler.sendMessage(player, builder.toString());
}
}
void SendAllHelpMessage(GenshinPlayer player, List<Command> annotations) {
if(player == null) {
StringBuilder builder = new StringBuilder("\nAvailable commands:\n");
annotations.forEach(annotation -> {
if (annotation.execution() != Command.Execution.PLAYER) {
builder.append(annotation.label()).append("\n");
builder.append(" ").append(annotation.description()).append("\n");
builder.append(" Usage: ").append(annotation.usage());
if (annotation.aliases().length >= 1) {
builder.append("\n").append(" Aliases: ");
for (String alias : annotation.aliases()) {
builder.append(alias).append(" ");
}
}
builder.append("\n");
}
});
CommandHandler.sendMessage(null, builder.toString());
} else {
CommandHandler.sendMessage(player, "Available commands:");
annotations.forEach(annotation -> {
if (annotation.execution() != Command.Execution.CONSOLE) {
StringBuilder builder = new StringBuilder(annotation.label()).append("\n");
builder.append(" ").append(annotation.description()).append("\n");
builder.append(" Usage: ").append(annotation.usage());
if (annotation.aliases().length >= 1) {
builder.append("\n").append(" Aliases: ");
for (String alias : annotation.aliases()) {
builder.append(alias).append(" ");
}
}
CommandHandler.sendMessage(player, builder.toString());
}
});
}
}
}
}
......@@ -8,6 +8,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.Utils;
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
import emu.grasscutter.data.custom.OpenConfigEntry;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.data.def.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
......@@ -18,6 +19,7 @@ public class GenshinData {
private static final Int2ObjectMap<String> abilityHashes = new Int2ObjectOpenHashMap<>();
private static final Map<String, AbilityEmbryoEntry> abilityEmbryos = new HashMap<>();
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
// ExcelConfigs
private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
......@@ -82,6 +84,10 @@ public class GenshinData {
return openConfigEntries;
}
public static Map<String, ScenePointEntry> getScenePointEntries() {
return scenePointEntries;
}
public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
return avatarDataMap;
}
......
......@@ -10,11 +10,15 @@ import java.util.regex.Pattern;
import emu.grasscutter.utils.Utils;
import org.reflections.Reflections;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.common.ScenePointConfig;
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
import emu.grasscutter.data.custom.OpenConfigEntry;
import emu.grasscutter.data.custom.ScenePointEntry;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
public class ResourceLoader {
......@@ -42,6 +46,7 @@ public class ResourceLoader {
loadOpenConfig();
// Load resources
loadResources();
loadScenePoints();
// Process into depots
GenshinDepot.load();
// Custom - TODO move this somewhere else
......@@ -121,6 +126,51 @@ public class ResourceLoader {
}
}
private static void loadScenePoints() {
Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)");
File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Scene/Point");
if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) {
Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!");
return;
}
List<ScenePointEntry> scenePointList = new ArrayList<>();
for (File file : folder.listFiles()) {
ScenePointConfig config = null;
Integer sceneId = null;
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
sceneId = Integer.parseInt(matcher.group(1));
} else {
continue;
}
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, ScenePointConfig.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
if (config.points == null) {
continue;
}
for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) {
PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class);
ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData);
scenePointList.add(sl);
}
for (ScenePointEntry entry : scenePointList) {
GenshinData.getScenePointEntries().put(entry.getName(), entry);
}
}
}
private static void loadAbilityEmbryos() {
// Read from cached file if exists
File embryoCache = new File(Grasscutter.getConfig().DATA_FOLDER + "AbilityEmbryos.json");
......
package emu.grasscutter.data.common;
public class PointData {
private pos tranPos;
public pos getTranPos() {
return tranPos;
}
public void setTranPos(pos tranPos) {
this.tranPos = tranPos;
}
public class pos {
private float x;
private float y;
private float z;
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getZ() {
return z;
}
public void setZ(float z) {
this.z = z;
}
}
}
package emu.grasscutter.data.common;
import com.google.gson.JsonObject;
public class ScenePointConfig {
public JsonObject points;
public JsonObject getPoints() {
return points;
}
public void setPoints(JsonObject Points) {
points = Points;
}
}
package emu.grasscutter.data.custom;
import emu.grasscutter.data.common.PointData;
public class ScenePointEntry {
private String name;
private PointData pointData;
public ScenePointEntry(String name, PointData pointData) {
this.name = name;
this.pointData = pointData;
}
public String getName() {
return name;
}
public PointData getPointData() {
return pointData;
}
}
......@@ -74,36 +74,36 @@ public class DatabaseHelper {
}
public static void saveAccount(Account account) {
DatabaseManager.getDatastore().save(account);
DatabaseManager.getAccountDatastore().save(account);
}
public static Account getAccountByName(String username) {
MorphiaCursor<Account> cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username).find(FIND_ONE);
MorphiaCursor<Account> cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username).find(FIND_ONE);
if (!cursor.hasNext()) return null;
return cursor.next();
}
public static Account getAccountByToken(String token) {
if (token == null) return null;
MorphiaCursor<Account> cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("token").equal(token).find(FIND_ONE);
MorphiaCursor<Account> cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("token").equal(token).find(FIND_ONE);
if (!cursor.hasNext()) return null;
return cursor.next();
}
public static Account getAccountById(String uid) {
MorphiaCursor<Account> cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("_id").equal(uid).find(FIND_ONE);
MorphiaCursor<Account> cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("_id").equal(uid).find(FIND_ONE);
if (!cursor.hasNext()) return null;
return cursor.next();
}
public static Account getAccountByPlayerId(int playerId) {
MorphiaCursor<Account> cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("playerId").equal(playerId).find(FIND_ONE);
MorphiaCursor<Account> cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("playerId").equal(playerId).find(FIND_ONE);
if (!cursor.hasNext()) return null;
return cursor.next();
}
public static boolean deleteAccount(String username) {
Query<Account> q = DatabaseManager.getDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username);
Query<Account> q = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username);
return DatabaseManager.getDatastore().findAndDelete(q) != null;
}
......
......@@ -17,7 +17,10 @@ import emu.grasscutter.game.inventory.GenshinItem;
public final class DatabaseManager {
private static MongoClient mongoClient;
private static MongoClient dispatchMongoClient;
private static Datastore datastore;
private static Datastore dispatchDatastore;
private static final Class<?>[] mappedClasses = new Class<?>[] {
DatabaseCounter.class, Account.class, GenshinPlayer.class, GenshinAvatar.class, GenshinItem.class, Friendship.class
......@@ -26,14 +29,24 @@ public final class DatabaseManager {
public static MongoClient getMongoClient() {
return mongoClient;
}
public static Datastore getDatastore() {
return datastore;
}
public static MongoDatabase getDatabase() {
public static Datastore getDatastore() {
return datastore;
}
public static MongoDatabase getDatabase() {
return getDatastore().getDatabase();
}
// Yes. I very dislike this method. However, this will be good for now.
// TODO: Add dispatch routes for player account management
public static Datastore getAccountDatastore() {
if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
return dispatchDatastore;
} else {
return datastore;
}
}
public static void initialize() {
// Initialize
......@@ -67,6 +80,28 @@ public final class DatabaseManager {
datastore.ensureIndexes();
}
}
if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
dispatchMongoClient = new MongoClient(new MongoClientURI(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl));
dispatchDatastore = morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection);
// Ensure indexes for dispatch server
try {
dispatchDatastore.ensureIndexes();
} catch (MongoCommandException e) {
Grasscutter.getLogger().info("Mongo index error: ", e);
// Duplicate index error
if (e.getCode() == 85) {
// Drop all indexes and re add them
MongoIterable<String> collections = dispatchDatastore.getDatabase().listCollectionNames();
for (String name : collections) {
dispatchDatastore.getDatabase().getCollection(name).dropIndexes();
}
// Add back indexes
dispatchDatastore.ensureIndexes();
}
}
}
}
public static synchronized int getNextId(Class<?> c) {
......
package emu.grasscutter.game;
import dev.morphia.annotations.AlsoLoad;
import dev.morphia.annotations.Collation;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
......@@ -24,7 +25,7 @@ public class Account {
private String username;
private String password; // Unused for now
private int playerId;
@AlsoLoad("playerUid") private int playerId;
private String email;
private String token;
......@@ -68,7 +69,7 @@ public class Account {
this.token = token;
}
public int getPlayerId() {
public int getPlayerUid() {
return this.playerId;
}
......
......@@ -31,6 +31,7 @@ import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
import emu.grasscutter.net.proto.PlayerApplyEnterMpReasonOuterClass.PlayerApplyEnterMpReason;
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.net.proto.WorldPlayerLocationInfoOuterClass.WorldPlayerLocationInfo;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketAbilityInvocationsNotify;
......@@ -38,6 +39,7 @@ import emu.grasscutter.server.packet.send.PacketAvatarAddNotify;
import emu.grasscutter.server.packet.send.PacketAvatarDataNotify;
import emu.grasscutter.server.packet.send.PacketAvatarGainCostumeNotify;
import emu.grasscutter.server.packet.send.PacketAvatarGainFlycloakNotify;
import emu.grasscutter.server.packet.send.PacketClientAbilityInitFinishNotify;
import emu.grasscutter.server.packet.send.PacketCombatInvocationsNotify;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
......@@ -48,9 +50,11 @@ import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
import emu.grasscutter.server.packet.send.PacketPlayerPropNotify;
import emu.grasscutter.server.packet.send.PacketPlayerStoreNotify;
import emu.grasscutter.server.packet.send.PacketPrivateChatNotify;
import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify;
import emu.grasscutter.server.packet.send.PacketSetNameCardRsp;
import emu.grasscutter.server.packet.send.PacketStoreWeightLimitNotify;
import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify;
import emu.grasscutter.server.packet.send.PacketWorldPlayerLocationNotify;
import emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify;
import emu.grasscutter.utils.Position;
......@@ -100,10 +104,12 @@ public class GenshinPlayer {
@Transient private int enterSceneToken;
@Transient private SceneLoadState sceneState;
@Transient private boolean hasSentAvatarDataNotify;
@Transient private long nextSendPlayerLocTime = 0;
@Transient private final Int2ObjectMap<CoopRequest> coopRequests;
@Transient private final InvokeHandler<CombatInvokeEntry> combatInvokeHandler;
@Transient private final InvokeHandler<AbilityInvokeEntry> abilityInvokeHandler;
@Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
@Deprecated @SuppressWarnings({ "rawtypes", "unchecked" }) // Morphia only!
public GenshinPlayer() {
......@@ -119,6 +125,12 @@ public class GenshinPlayer {
}
this.properties.put(prop.getId(), 0);
}
this.gachaInfo = new PlayerGachaInfo();
this.nameCardList = new HashSet<>();
this.flyCloakList = new HashSet<>();
this.costumeList = new HashSet<>();
this.setSceneId(3);
this.setRegionId(1);
this.sceneState = SceneLoadState.NONE;
......@@ -126,6 +138,7 @@ public class GenshinPlayer {
this.coopRequests = new Int2ObjectOpenHashMap<>();
this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class);
this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class);
this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class);
}
// On player creation
......@@ -137,11 +150,6 @@ public class GenshinPlayer {
this.nickname = "Traveler";
this.signature = "";
this.teamManager = new TeamManager(this);
this.gachaInfo = new PlayerGachaInfo();
this.playerProfile = new PlayerProfile(this);
this.nameCardList = new HashSet<>();
this.flyCloakList = new HashSet<>();
this.costumeList = new HashSet<>();
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1);
this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1);
this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50);
......@@ -285,7 +293,7 @@ public class GenshinPlayer {
}
private float getExpModifier() {
return Grasscutter.getConfig().getGameRates().ADVENTURE_EXP_RATE;
return Grasscutter.getConfig().getGameServerOptions().getGameRates().ADVENTURE_EXP_RATE;
}
// Affected by exp rate
......@@ -344,7 +352,6 @@ public class GenshinPlayer {
public PlayerProfile getProfile() {
if (this.playerProfile == null) {
this.playerProfile = new PlayerProfile(this);
this.save();
}
return playerProfile;
}
......@@ -389,6 +396,10 @@ public class GenshinPlayer {
return this.abilityInvokeHandler;
}
public InvokeHandler<AbilityInvokeEntry> getClientAbilityInitFinishHandler() {
return clientAbilityInitFinishHandler;
}
public void setMpSetting(MpSettingType mpSetting) {
this.mpSetting = mpSetting;
}
......@@ -647,6 +658,13 @@ public class GenshinPlayer {
return social;
}
public WorldPlayerLocationInfo getWorldPlayerLocationInfo() {
return WorldPlayerLocationInfo.newBuilder()
.setSceneId(this.getSceneId())
.setPlayerLoc(this.getPlayerLocationInfo())
.build();
}
public PlayerLocationInfo getPlayerLocationInfo() {
return PlayerLocationInfo.newBuilder()
.setUid(this.getUid())
......@@ -672,9 +690,22 @@ public class GenshinPlayer {
}
// Ping
if (this.getWorld() != null) {
this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld())); // Player ping
// RTT notify - very important to send this often
this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld()));
// Update player locations if in multiplayer every 5 seconds
long time = System.currentTimeMillis();
if (this.getWorld().isMultiplayer() && this.getScene() != null && time > nextSendPlayerLocTime) {
this.sendPacket(new PacketWorldPlayerLocationNotify(this.getWorld()));
this.sendPacket(new PacketScenePlayerLocationNotify(this.getScene()));
this.resetSendPlayerLocTime();
}
}
}
public void resetSendPlayerLocTime() {
this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000;
}
@PostLoad
private void onLoad() {
......@@ -689,12 +720,8 @@ public class GenshinPlayer {
// Make sure these exist
if (this.getTeamManager() == null) {
this.teamManager = new TeamManager(this);
} if (this.getGachaInfo() == null) {
this.gachaInfo = new PlayerGachaInfo();
} if (this.nameCardList == null) {
this.nameCardList = new HashSet<>();
} if (this.costumeList == null) {
this.costumeList = new HashSet<>();
} if (this.getProfile().getUid() == 0) {
this.getProfile().syncWithCharacter(this);
}
// Check if player object exists in server
......
......@@ -34,7 +34,8 @@ public class GenshinScene {
private int time;
private ClimateType climate;
private int weather;
public GenshinScene(World world, SceneData sceneData) {
this.world = world;
this.sceneData = sceneData;
......@@ -89,10 +90,18 @@ public class GenshinScene {
return climate;
}
public int getWeather() {
return weather;
}
public void setClimate(ClimateType climate) {
this.climate = climate;
}
public void setWeather(int weather) {
this.weather = weather;
}
public boolean isInScene(GenshinEntity entity) {
return this.entities.containsKey(entity.getId());
}
......
......@@ -13,7 +13,7 @@ public class TeamInfo {
public TeamInfo() {
this.name = "";
this.avatars = new ArrayList<>(Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam);
this.avatars = new ArrayList<>(Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam);
}
public String getName() {
......@@ -37,7 +37,7 @@ public class TeamInfo {
}
public boolean addAvatar(GenshinAvatar avatar) {
if (size() >= Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam || contains(avatar)) {
if (size() >= Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam || contains(avatar)) {
return false;
}
......@@ -57,7 +57,7 @@ public class TeamInfo {
}
public void copyFrom(TeamInfo team) {
copyFrom(team, Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam);
copyFrom(team, Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam);
}
public void copyFrom(TeamInfo team, int maxTeamSize) {
......
......@@ -164,13 +164,13 @@ public class TeamManager {
public int getMaxTeamSize() {
if (getPlayer().isInMultiplayer()) {
int max = Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeamMultiplayer;
int max = Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeamMultiplayer;
if (getPlayer().getWorld().getHost() == this.getPlayer()) {
return Math.max(1, (int) Math.ceil(max / (double) getWorld().getPlayerCount()));
}
return Math.max(1, (int) Math.floor(max / (double) getWorld().getPlayerCount()));
}
return Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam;
return Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam;
}
// Methods
......
......@@ -208,11 +208,14 @@ public class World implements Iterable<GenshinPlayer> {
}
public boolean transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) {
if (player.getScene().getId() == sceneId || GenshinData.getSceneDataMap().get(sceneId) == null) {
if (GenshinData.getSceneDataMap().get(sceneId) == null) {
return false;
}
Integer oldSceneId = null;
if (player.getScene() != null) {
oldSceneId = player.getScene().getId();
player.getScene().removePlayer(player);
}
......@@ -221,7 +224,11 @@ public class World implements Iterable<GenshinPlayer> {
player.getPos().set(pos);
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterSelf, EnterReason.TransPoint, sceneId, pos));
if (oldSceneId.equals(sceneId)) {
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterGoto, EnterReason.TransPoint, sceneId, pos));
} else {
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterJump, EnterReason.TransPoint, sceneId, pos));
}
return true;
}
......
......@@ -13,6 +13,7 @@ import dev.morphia.annotations.Indexed;
import dev.morphia.annotations.PostLoad;
import dev.morphia.annotations.PrePersist;
import dev.morphia.annotations.Transient;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GenshinData;
import emu.grasscutter.data.common.FightPropData;
import emu.grasscutter.data.custom.OpenConfigEntry;
......@@ -42,6 +43,7 @@ import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo;
import emu.grasscutter.net.proto.AvatarInfoOuterClass.AvatarInfo;
import emu.grasscutter.server.packet.send.PacketAbilityChangeNotify;
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropNotify;
import emu.grasscutter.utils.ProtoHelper;
......@@ -69,7 +71,7 @@ public class GenshinAvatar {
@Transient private final Int2ObjectMap<GenshinItem> equips;
@Transient private final Int2FloatOpenHashMap fightProp;
@Transient private final Set<String> bonusAbilityList;
@Transient private Set<String> extraAbilityEmbryos;
private Map<Integer, Integer> skillLevelMap; // Talent levels
private Map<Integer, Integer> proudSkillBonusMap; // Talent bonus levels (from const)
......@@ -86,7 +88,7 @@ public class GenshinAvatar {
// Morhpia only!
this.equips = new Int2ObjectOpenHashMap<>();
this.fightProp = new Int2FloatOpenHashMap();
this.bonusAbilityList = new HashSet<>();
this.extraAbilityEmbryos = new HashSet<>();
this.proudSkillBonusMap = new HashMap<>(); // TODO Move to genshin avatar
}
......@@ -260,8 +262,8 @@ public class GenshinAvatar {
return proudSkillBonusMap;
}
public Set<String> getBonusAbilityList() {
return bonusAbilityList;
public Set<String> getExtraAbilityEmbryos() {
return extraAbilityEmbryos;
}
public float getCurrentHp() {
......@@ -347,14 +349,14 @@ public class GenshinAvatar {
item.setEquipCharacter(this.getAvatarId());
item.save();
if (this.getPlayer().hasSentAvatarDataNotify()) {
this.getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(this, item));
}
if (shouldRecalc) {
this.recalcStats();
}
if (this.getPlayer().hasSentAvatarDataNotify()) {
this.getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(this, item));
}
return true;
}
......@@ -371,11 +373,18 @@ public class GenshinAvatar {
}
public void recalcStats() {
recalcStats(false);
}
public void recalcStats(boolean forceSendAbilityChange) {
// Setup
AvatarData data = this.getAvatarData();
AvatarPromoteData promoteData = GenshinData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel());
Int2IntOpenHashMap setMap = new Int2IntOpenHashMap();
this.getBonusAbilityList().clear();
// Extra ability embryos
Set<String> prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos();
this.extraAbilityEmbryos = new HashSet<>();
// Get hp percent, set to 100% if none
float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
......@@ -458,7 +467,7 @@ public class GenshinAvatar {
}
// Add any skill strings from this affix
this.addToAbilityList(affix.getOpenConfig(), true);
this.addToExtraAbilityEmbryos(affix.getOpenConfig(), true);
} else {
break;
}
......@@ -505,7 +514,7 @@ public class GenshinAvatar {
}
// Add any skill strings from this affix
this.addToAbilityList(affix.getOpenConfig(), true);
this.addToExtraAbilityEmbryos(affix.getOpenConfig(), true);
}
}
}
......@@ -538,7 +547,7 @@ public class GenshinAvatar {
}
// Add any skill strings from this proud skill
this.addToAbilityList(proudSkillData.getOpenConfig(), true);
this.addToExtraAbilityEmbryos(proudSkillData.getOpenConfig(), true);
}
// Constellations
......@@ -550,7 +559,7 @@ public class GenshinAvatar {
}
// Add any skill strings from this constellation
this.addToAbilityList(avatarTalentData.getOpenConfig(), false);
this.addToExtraAbilityEmbryos(avatarTalentData.getOpenConfig(), false);
}
}
......@@ -573,11 +582,17 @@ public class GenshinAvatar {
// Packet
if (getPlayer() != null && getPlayer().hasSentAvatarDataNotify()) {
// Update stats for client
getPlayer().sendPacket(new PacketAvatarFightPropNotify(this));
// Update client abilities
EntityAvatar entity = this.getAsEntity();
if (entity != null && (!this.getExtraAbilityEmbryos().equals(prevExtraAbilityEmbryos) || forceSendAbilityChange)) {
getPlayer().sendPacket(new PacketAbilityChangeNotify(entity));
}
}
}
public void addToAbilityList(String openConfig, boolean forceAdd) {
public void addToExtraAbilityEmbryos(String openConfig, boolean forceAdd) {
if (openConfig == null || openConfig.length() == 0) {
return;
}
......@@ -586,14 +601,14 @@ public class GenshinAvatar {
if (entry == null) {
if (forceAdd) {
// Add config string to ability skill list anyways
this.getBonusAbilityList().add(openConfig);
this.getExtraAbilityEmbryos().add(openConfig);
}
return;
}
if (entry.getAddAbilities() != null) {
for (String ability : entry.getAddAbilities()) {
this.getBonusAbilityList().add(ability);
this.getExtraAbilityEmbryos().add(ability);
}
}
}
......
......@@ -223,8 +223,8 @@ public class EntityAvatar extends GenshinEntity {
}
}
// Add equip abilities
if (this.getAvatar().getBonusAbilityList().size() > 0) {
for (String skill : this.getAvatar().getBonusAbilityList()) {
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(Utils.abilityHash(skill))
......
......@@ -220,7 +220,7 @@ public class FriendsList {
friendship.setOwner(getPlayer());
// Check if friend is online
GenshinPlayer friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getId());
GenshinPlayer friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getUid());
if (friend != null) {
// Set friend to online mode
friendship.setFriendProfile(friend);
......
......@@ -88,7 +88,7 @@ public class Friendship {
public FriendBrief toProto() {
FriendBrief proto = FriendBrief.newBuilder()
.setUid(getFriendProfile().getId())
.setUid(getFriendProfile().getUid())
.setNickname(getFriendProfile().getName())
.setLevel(getFriendProfile().getPlayerLevel())
.setAvatar(HeadImage.newBuilder().setAvatarId(getFriendProfile().getAvatarId()))
......
......@@ -7,7 +7,7 @@ import emu.grasscutter.utils.Utils;
public class PlayerProfile {
@Transient private GenshinPlayer player;
private int id;
@AlsoLoad("id") private int uid;
private int nameCard;
private int avatarId;
private String name;
......@@ -22,12 +22,12 @@ public class PlayerProfile {
public PlayerProfile() { }
public PlayerProfile(GenshinPlayer player) {
this.id = player.getUid();
this.uid = player.getUid();
this.syncWithCharacter(player);
}
public int getId() {
return id;
public int getUid() {
return uid;
}
public GenshinPlayer getPlayer() {
......@@ -87,6 +87,7 @@ public class PlayerProfile {
return;
}
this.uid = player.getUid();
this.name = player.getNickname();
this.avatarId = player.getHeadImage();
this.signature = player.getSignature();
......
......@@ -92,7 +92,7 @@ public class GachaBanner {
}
public GachaInfo toProto() {
String record = "http://" + (Grasscutter.getConfig().DispatchServerPublicIp.isEmpty() ? Grasscutter.getConfig().DispatchServerIp : Grasscutter.getConfig().DispatchServerPublicIp) + "/gacha";
String record = "http://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + "/gacha";
GachaInfo.Builder info = GachaInfo.newBuilder()
.setGachaType(this.getGachaType())
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment