Commit 9e7c9f46 authored by Benjamin Elsdon's avatar Benjamin Elsdon
Browse files

Merge remote-tracking branch 'upstream/development' into dev-mail

parents cb03b654 d612530c
...@@ -56,7 +56,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; ...@@ -56,7 +56,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@Entity(value = "avatars", noClassnameStored = true) @Entity(value = "avatars", useDiscriminator = false)
public class GenshinAvatar { public class GenshinAvatar {
@Id private ObjectId id; @Id private ObjectId id;
@Indexed private int ownerId; // Id of player that this avatar belongs to @Indexed private int ownerId; // Id of player that this avatar belongs to
......
...@@ -9,7 +9,7 @@ import emu.grasscutter.net.proto.FriendBriefOuterClass.FriendBrief; ...@@ -9,7 +9,7 @@ import emu.grasscutter.net.proto.FriendBriefOuterClass.FriendBrief;
import emu.grasscutter.net.proto.FriendOnlineStateOuterClass.FriendOnlineState; import emu.grasscutter.net.proto.FriendOnlineStateOuterClass.FriendOnlineState;
import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage; import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage;
@Entity(value = "friendships", noClassnameStored = true) @Entity(value = "friendships", useDiscriminator = false)
public class Friendship { public class Friendship {
@Id private ObjectId id; @Id private ObjectId id;
......
...@@ -4,6 +4,7 @@ import dev.morphia.annotations.*; ...@@ -4,6 +4,7 @@ import dev.morphia.annotations.*;
import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
@Entity
public class PlayerProfile { public class PlayerProfile {
@Transient private GenshinPlayer player; @Transient private GenshinPlayer player;
......
package emu.grasscutter.game.gacha; package emu.grasscutter.game.gacha;
import dev.morphia.annotations.Entity;
@Entity
public class PlayerGachaBannerInfo { public class PlayerGachaBannerInfo {
private int pity5 = 0; private int pity5 = 0;
private int pity4 = 0; private int pity4 = 0;
......
package emu.grasscutter.game.gacha; package emu.grasscutter.game.gacha;
import dev.morphia.annotations.Entity;
@Entity
public class PlayerGachaInfo { public class PlayerGachaInfo {
private PlayerGachaBannerInfo standardBanner; private PlayerGachaBannerInfo standardBanner;
private PlayerGachaBannerInfo eventCharacterBanner; private PlayerGachaBannerInfo eventCharacterBanner;
......
...@@ -34,7 +34,7 @@ import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo; ...@@ -34,7 +34,7 @@ import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
import emu.grasscutter.net.proto.WeaponOuterClass.Weapon; import emu.grasscutter.net.proto.WeaponOuterClass.Weapon;
import emu.grasscutter.utils.WeightedList; import emu.grasscutter.utils.WeightedList;
@Entity(value = "items", noClassnameStored = true) @Entity(value = "items", useDiscriminator = false)
public class GenshinItem { public class GenshinItem {
@Id private ObjectId id; @Id private ObjectId id;
@Indexed private int ownerId; @Indexed private int ownerId;
......
package emu.grasscutter.game.player;
import dev.morphia.annotations.Entity;
import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday;
@Entity
public class PlayerBirthday {
private int day;
private int month;
public PlayerBirthday(){
this.day = 0;
this.month = 0;
}
public PlayerBirthday(int day, int month){
this.day = day;
this.month = month;
}
public PlayerBirthday set(PlayerBirthday birth){
this.day = birth.day;
this.month = birth.month;
return this;
}
public PlayerBirthday set(int d, int m){
this.day = d;
this.month = m;
return this;
}
public PlayerBirthday setDay(int value){
this.day = value;
return this;
}
public PlayerBirthday setMonth(int value){
this.month = value;
return this;
}
public int getDay(){
return this.day;
}
public int getMonth(){
return this.month;
}
public Birthday toProto(){
return Birthday.newBuilder()
.setDay(this.getDay())
.setMonth(this.getMonth())
.build();
}
public Birthday.Builder getFilledProtoWhenNotEmpty(){
if(this.getDay() > 0)
{
return Birthday.newBuilder()
.setDay(this.getDay())
.setMonth(this.getMonth());
}
return Birthday.newBuilder();
}
}
\ No newline at end of file
...@@ -1034,6 +1034,8 @@ public class PacketOpcodes { ...@@ -1034,6 +1034,8 @@ public class PacketOpcodes {
public static final int ShowTemplateReminderNotify = 3164; public static final int ShowTemplateReminderNotify = 3164;
public static final int SignInInfoReq = 2510; public static final int SignInInfoReq = 2510;
public static final int SignInInfoRsp = 2515; public static final int SignInInfoRsp = 2515;
public static final int SitReq = 354;
public static final int SitRsp = 335;
public static final int SocialDataNotify = 4063; public static final int SocialDataNotify = 4063;
public static final int SpringUseReq = 1720; public static final int SpringUseReq = 1720;
public static final int SpringUseRsp = 1727; public static final int SpringUseRsp = 1727;
...@@ -1208,5 +1210,4 @@ public class PacketOpcodes { ...@@ -1208,5 +1210,4 @@ public class PacketOpcodes {
public static final int WorldRoutineChangeNotify = 3548; public static final int WorldRoutineChangeNotify = 3548;
public static final int WorldRoutineTypeCloseNotify = 3513; public static final int WorldRoutineTypeCloseNotify = 3513;
public static final int WorldRoutineTypeRefreshNotify = 3545; public static final int WorldRoutineTypeRefreshNotify = 3545;
} }
\ No newline at end of file
...@@ -64,9 +64,8 @@ public class MihoyoKcpServer extends Thread { ...@@ -64,9 +64,8 @@ public class MihoyoKcpServer extends Thread {
// Wait until the server socket is closed. // Wait until the server socket is closed.
f.channel().closeFuture().sync(); f.channel().closeFuture().sync();
} catch (Exception e) { } catch (Exception exception) {
// TODO Auto-generated catch block Grasscutter.getLogger().error("Unable to start game server.", exception);
e.printStackTrace();
} finally { } finally {
// Close // Close
finish(); finish();
......
...@@ -24,6 +24,7 @@ import emu.grasscutter.utils.Utils; ...@@ -24,6 +24,7 @@ import emu.grasscutter.utils.Utils;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import java.io.*; import java.io.*;
import java.net.BindException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.net.URLDecoder; import java.net.URLDecoder;
...@@ -101,14 +102,14 @@ public final class DispatchServer { ...@@ -101,14 +102,14 @@ public final class DispatchServer {
.setName("os_usa") .setName("os_usa")
.setTitle(Grasscutter.getConfig().getGameServerOptions().Name) .setTitle(Grasscutter.getConfig().getGameServerOptions().Name)
.setType("DEV_PUBLIC") .setType("DEV_PUBLIC")
.setDispatchUrl("https://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region_" + defaultServerName) .setDispatchUrl("http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + (Grasscutter.getConfig().getDispatchOptions().PublicPort != 0 ? Grasscutter.getConfig().getDispatchOptions().PublicPort : Grasscutter.getConfig().getDispatchOptions().Port) + "/query_cur_region_" + defaultServerName)
.build(); .build();
usedNames.add(defaultServerName); usedNames.add(defaultServerName);
servers.add(server); servers.add(server);
RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder()
.setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp)) .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp))
.setPort(Grasscutter.getConfig().getGameServerOptions().Port) .setPort(Grasscutter.getConfig().getGameServerOptions().PublicPort != 0 ? Grasscutter.getConfig().getGameServerOptions().PublicPort : Grasscutter.getConfig().getGameServerOptions().Port)
.setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin")))
.build(); .build();
...@@ -131,7 +132,7 @@ public final class DispatchServer { ...@@ -131,7 +132,7 @@ public final class DispatchServer {
.setName(regionInfo.Name) .setName(regionInfo.Name)
.setTitle(regionInfo.Title) .setTitle(regionInfo.Title)
.setType("DEV_PUBLIC") .setType("DEV_PUBLIC")
.setDispatchUrl("https://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region_" + regionInfo.Name) .setDispatchUrl("http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region_" + regionInfo.Name)
.build(); .build();
usedNames.add(regionInfo.Name); usedNames.add(regionInfo.Name);
servers.add(server); servers.add(server);
...@@ -159,11 +160,20 @@ public final class DispatchServer { ...@@ -159,11 +160,20 @@ public final class DispatchServer {
} }
} }
private HttpServer safelyCreateServer(InetSocketAddress address) {
try {
return HttpServer.create(address, 0);
} catch (BindException ignored) {
Grasscutter.getLogger().error("Unable to bind to port: " + getAddress().getPort() + " (HTTP)");
} catch (Exception exception) {
Grasscutter.getLogger().error("Unable to start HTTP server.", exception);
} return null;
}
public void start() throws Exception { public void start() throws Exception {
HttpServer server; HttpServer server;
if (Grasscutter.getConfig().getDispatchOptions().UseSSL) { if (Grasscutter.getConfig().getDispatchOptions().UseSSL) {
HttpsServer httpsServer; HttpsServer httpsServer = HttpsServer.create(getAddress(), 0);
httpsServer = HttpsServer.create(getAddress(), 0);
SSLContext sslContext = SSLContext.getInstance("TLS"); SSLContext sslContext = SSLContext.getInstance("TLS");
try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) { try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) {
char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray(); char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray();
...@@ -176,15 +186,21 @@ public final class DispatchServer { ...@@ -176,15 +186,21 @@ public final class DispatchServer {
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
server = httpsServer; server = httpsServer;
} catch (BindException ignored) {
Grasscutter.getLogger().error("Unable to bind to port: " + getAddress().getPort() + " (HTTPS)");
server = this.safelyCreateServer(this.getAddress());
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().warn("[Dispatch] No SSL cert found! Falling back to HTTP server."); Grasscutter.getLogger().warn("[Dispatch] No SSL cert found! Falling back to HTTP server.");
Grasscutter.getConfig().getDispatchOptions().UseSSL = false; Grasscutter.getConfig().getDispatchOptions().UseSSL = false;
server = HttpServer.create(getAddress(), 0); server = this.safelyCreateServer(this.getAddress());
} }
} else { } else {
server = HttpServer.create(getAddress(), 0); server = this.safelyCreateServer(this.getAddress());
} }
if(server == null)
throw new NullPointerException("An HTTP server was not created.");
server.createContext("/", t -> responseHTML(t, "Hello")); server.createContext("/", t -> responseHTML(t, "Hello"));
// Dispatch // Dispatch
......
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.EvtAvatarSitDownNotifyOuterClass.EvtAvatarSitDownNotify;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketEvtAvatarSitDownNotify;
@Opcodes(PacketOpcodes.EvtAvatarSitDownNotify)
public class HandleEvtAvatarSitDownNotify extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
EvtAvatarSitDownNotify notify = EvtAvatarSitDownNotify.parseFrom(payload);
session.send(new PacketEvtAvatarSitDownNotify(notify));
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SitReqOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketSitRsp;
import emu.grasscutter.utils.Position;
@Opcodes(PacketOpcodes.SitReq)
public class HandleSitReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SitReqOuterClass.SitReq req = SitReqOuterClass.SitReq.parseFrom(payload);
float x = req.getPosition().getX();
float y = req.getPosition().getY();
float z = req.getPosition().getZ();
session.send(new PacketSitRsp(req.getChairId(), new Position(x, y, z), session.getPlayer().getTeamManager().getCurrentAvatarEntity().getId()));
}
}
\ No newline at end of file
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.entity.GenshinEntity;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SceneEntityDrownReqOuterClass.SceneEntityDrownReq;
import emu.grasscutter.net.proto.VisionTypeOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityDrownRsp;
@Opcodes(PacketOpcodes.SceneEntityDrownReq)
public class HandlerSceneEntityDrownReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SceneEntityDrownReq req = SceneEntityDrownReq.parseFrom(payload);
GenshinEntity entity = session.getPlayer().getScene().getEntityById(req.getEntityId());
PacketLifeStateChangeNotify lifeStateChangeNotify = new PacketLifeStateChangeNotify(entity, entity, LifeState.LIFE_DEAD);
PacketSceneEntityDrownRsp drownRsp = new PacketSceneEntityDrownRsp(req.getEntityId());
//kill entity + broadcast it
session.getPlayer().getScene().broadcastPacket(lifeStateChangeNotify);
session.getPlayer().getScene().broadcastPacket(drownRsp);
//TODO: make a list somewhere of all entities to remove per tick rather than one by one
session.getPlayer().getScene().removeEntity(entity, VisionTypeOuterClass.VisionType.VisionDie);
}
}
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketGetPlayerSocialDetailRsp;
import emu.grasscutter.server.packet.send.PacketSetPlayerBirthdayRsp;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq;
import com.google.gson.Gson;
@Opcodes(PacketOpcodes.SetPlayerBirthdayReq)
public class HandlerSetPlayerBirthdayReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SetPlayerBirthdayReq req = SetPlayerBirthdayReq.parseFrom(payload);
if(req.getBirth() != null && req.getBirth().getDay() > 0 && req.getBirth().getMonth() > 0)
{
int day = req.getBirth().getDay();
int month = req.getBirth().getMonth();
// Update birthday value
session.getPlayer().setBirthday(day, month);
// Save birthday month and day
session.getPlayer().save();
SocialDetail.Builder detail = session.getPlayer().getSocialDetail();
session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer()));
session.send(new PacketGetPlayerSocialDetailRsp(detail));
}
}
}
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.EvtAvatarSitDownNotifyOuterClass.EvtAvatarSitDownNotify;
public class PacketEvtAvatarSitDownNotify extends GenshinPacket {
public PacketEvtAvatarSitDownNotify(EvtAvatarSitDownNotify notify) {
super(PacketOpcodes.EvtAvatarSitDownNotify);
EvtAvatarSitDownNotify proto = EvtAvatarSitDownNotify.newBuilder()
.setEntityId(notify.getEntityId())
.setPosition(notify.getPosition())
.setChairId(notify.getChairId())
.build();
this.setData(proto);
}
}
...@@ -41,7 +41,7 @@ public class PacketPlayerLoginRsp extends GenshinPacket { ...@@ -41,7 +41,7 @@ public class PacketPlayerLoginRsp extends GenshinPacket {
RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder()
.setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp)) .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp))
.setPort(Grasscutter.getConfig().getGameServerOptions().Port) .setPort(Grasscutter.getConfig().getGameServerOptions().PublicPort != 0 ? Grasscutter.getConfig().getGameServerOptions().PublicPort : Grasscutter.getConfig().getGameServerOptions().Port)
.setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin")))
.build(); .build();
......
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.entity.GenshinEntity;
import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SceneEntityDrownRspOuterClass.SceneEntityDrownRsp;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
public class PacketSceneEntityDrownRsp extends GenshinPacket {
public PacketSceneEntityDrownRsp(int entityId) {
super(PacketOpcodes.SceneEntityDrownRsp);
SceneEntityDrownRsp proto = new SceneEntityDrownRsp().toBuilder().setEntityId(entityId).build();
this.setData(proto);
}
}
package emu.grasscutter.server.packet.send;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp;
import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday;
public class PacketSetPlayerBirthdayRsp extends GenshinPacket {
public PacketSetPlayerBirthdayRsp(GenshinPlayer player) {
super(PacketOpcodes.SetPlayerBirthdayRsp);
SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder()
.setBirth(player.getBirthday().toProto())
.build();
this.setData(proto);
}
}
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SitRspOuterClass.SitRsp;
import emu.grasscutter.utils.Position;
public class PacketSitRsp extends GenshinPacket {
public PacketSitRsp(long chairId, Position pos, int EntityId) {
super(PacketOpcodes.SitRsp);
SitRsp proto = SitRsp.newBuilder()
.setEntityId(EntityId)
.setPosition(pos.toProto())
.setChairId(chairId)
.build();
this.setData(proto);
}
}
package emu.grasscutter.tools; package emu.grasscutter.tools;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -30,13 +35,13 @@ public final class Tools { ...@@ -30,13 +35,13 @@ public final class Tools {
ResourceLoader.loadResources(); ResourceLoader.loadResources();
Map<Long, String> map; Map<Long, String> map;
try (FileReader fileReader = new FileReader(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "TextMap/TextMapEN.json"))) { try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "TextMap/TextMapEN.json")), StandardCharsets.UTF_8)) {
map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken<Map<Long, String>>() {}.getType()); map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken<Map<Long, String>>() {}.getType());
} }
List<Integer> list; List<Integer> list;
String fileName = "./GM Handbook.txt"; String fileName = "./GM Handbook.txt";
try (FileWriter fileWriter = new FileWriter(fileName); PrintWriter writer = new PrintWriter(fileWriter)) { try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8), false)) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
......
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