Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
ziqian zhang
Grasscutter
Commits
16318b37
Commit
16318b37
authored
Apr 21, 2022
by
Melledy
Committed by
GitHub
Apr 21, 2022
Browse files
Merge pull request #74 from 4Benj/development
Server run modes and multi-server dispatch support
parents
8b5d7855
23caf994
Changes
17
Hide whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/Config.java
View file @
16318b37
package
emu.grasscutter
;
import
java.util.ArrayList
;
public
final
class
Config
{
public
String
DispatchServerIp
=
"0.0.0.0"
;
public
String
DispatchServerPublicIp
=
"127.0.0.1"
;
public
int
DispatchServerPort
=
443
;
public
String
DispatchServerKeystorePath
=
"./keystore.p12"
;
public
String
DispatchServerKeystorePassword
=
""
;
public
Boolean
UseSSL
=
true
;
public
String
GameServerName
=
"Test"
;
public
String
GameServerIp
=
"0.0.0.0"
;
public
String
GameServerPublicIp
=
"127.0.0.1"
;
public
int
GameServerPort
=
22102
;
public
String
DatabaseUrl
=
"mongodb://localhost:27017"
;
public
String
DatabaseCollection
=
"grasscutter"
;
...
...
@@ -21,26 +12,52 @@ public final class Config {
public
String
PACKETS_FOLDER
=
"./packets/"
;
public
String
DUMPS_FOLDER
=
"./dumps/"
;
public
String
KEY_FOLDER
=
"./keys/"
;
public
boolean
LOG_PACKETS
=
false
;
public
GameRates
Game
=
new
GameRates
();
public
ServerOptions
ServerOptions
=
new
ServerOptions
();
public
String
RunMode
=
"HYBRID"
;
// HYBRID, DISPATCH_ONLY, GAME_ONLY
public
GameServerOptions
GameServer
=
new
GameServerOptions
();
public
DispatchServerOptions
DispatchServer
=
new
DispatchServerOptions
();
public
Game
Rates
getGameRate
s
()
{
return
Game
;
public
Game
ServerOptions
getGameServerOption
s
()
{
return
Game
Server
;
}
public
ServerOptions
getServerOptions
()
{
return
ServerOptions
;
}
public
DispatchServerOptions
getDispatchOptions
()
{
return
DispatchServer
;
}
public
static
class
DispatchServerOptions
{
public
String
Ip
=
"0.0.0.0"
;
public
String
PublicIp
=
"127.0.0.1"
;
public
int
Port
=
443
;
public
String
KeystorePath
=
"./keystore.p12"
;
public
String
KeystorePassword
=
""
;
public
Boolean
UseSSL
=
true
;
public
boolean
AutomaticallyCreateAccounts
=
false
;
public
RegionInfo
[]
GameServers
=
{};
public
RegionInfo
[]
getGameServers
()
{
return
GameServers
;
}
public
static
class
GameRates
{
public
float
ADVENTURE_EXP_RATE
=
1.0f
;
public
float
MORA_RATE
=
1.0f
;
public
float
DOMAIN_DROP_RATE
=
1.0f
;
public
static
class
RegionInfo
{
public
String
Name
=
"os_usa"
;
public
String
Title
=
"Test"
;
public
String
Ip
=
"127.0.0.1"
;
public
int
Port
=
22102
;
}
}
public
static
class
GameServerOptions
{
public
String
Name
=
"Test"
;
public
String
Ip
=
"0.0.0.0"
;
public
String
PublicIp
=
"127.0.0.1"
;
public
int
Port
=
22102
;
public
String
DispatchServerDatabaseUrl
=
"mongodb://localhost:27017"
;
public
String
DispatchServerDatabaseCollection
=
"grasscutter"
;
public
boolean
LOG_PACKETS
=
false
;
public
static
class
ServerOptions
{
public
int
InventoryLimitWeapon
=
2000
;
public
int
InventoryLimitRelic
=
2000
;
public
int
InventoryLimitMaterial
=
2000
;
...
...
@@ -52,6 +69,15 @@ public final class Config {
public
boolean
WatchGacha
=
false
;
public
int
[]
WelcomeEmotes
=
{
2007
,
1002
,
4010
};
public
String
WelcomeMotd
=
"Welcome to Grasscutter emu"
;
public
boolean
AutomaticallyCreateAccounts
=
false
;
public
GameRates
Game
=
new
GameRates
();
public
GameRates
getGameRates
()
{
return
Game
;
}
public
static
class
GameRates
{
public
float
ADVENTURE_EXP_RATE
=
1.0f
;
public
float
MORA_RATE
=
1.0f
;
public
float
DOMAIN_DROP_RATE
=
1.0f
;
}
}
}
src/main/java/emu/grasscutter/Grasscutter.java
View file @
16318b37
...
...
@@ -73,11 +73,26 @@ public final class Grasscutter {
DatabaseManager
.
initialize
();
// Start servers.
dispatchServer
=
new
DispatchServer
();
dispatchServer
.
start
();
gameServer
=
new
GameServer
(
new
InetSocketAddress
(
getConfig
().
GameServerIp
,
getConfig
().
GameServerPort
));
gameServer
.
start
();
if
(
getConfig
().
RunMode
.
equalsIgnoreCase
(
"HYBRID"
))
{
dispatchServer
=
new
DispatchServer
();
dispatchServer
.
start
();
gameServer
=
new
GameServer
(
new
InetSocketAddress
(
getConfig
().
getGameServerOptions
().
Ip
,
getConfig
().
getGameServerOptions
().
Port
));
gameServer
.
start
();
}
else
if
(
getConfig
().
RunMode
.
equalsIgnoreCase
(
"DISPATCH_ONLY"
))
{
dispatchServer
=
new
DispatchServer
();
dispatchServer
.
start
();
}
else
if
(
getConfig
().
RunMode
.
equalsIgnoreCase
(
"GAME_ONLY"
))
{
gameServer
=
new
GameServer
(
new
InetSocketAddress
(
getConfig
().
getGameServerOptions
().
Ip
,
getConfig
().
getGameServerOptions
().
Port
));
gameServer
.
start
();
}
else
{
getLogger
().
error
(
"Invalid server run mode. "
+
getConfig
().
RunMode
);
getLogger
().
error
(
"Server run mode must be 'HYBRID', 'DISPATCH_ONLY', or 'GAME_ONLY'. Unable to start Grasscutter..."
);
getLogger
().
error
(
"Shutting down..."
);
System
.
exit
(
1
);
}
// Open console.
startConsole
();
...
...
@@ -106,9 +121,14 @@ public final class Grasscutter {
try
(
BufferedReader
br
=
new
BufferedReader
(
new
InputStreamReader
(
System
.
in
)))
{
while
((
input
=
br
.
readLine
())
!=
null
)
{
try
{
if
(
getConfig
().
RunMode
.
equalsIgnoreCase
(
"DISPATCH_ONLY"
))
{
getLogger
().
error
(
"Commands are not supported in dispatch only mode"
);
return
;
}
CommandMap
.
getInstance
().
invoke
(
null
,
input
);
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"Command error: "
+
e
.
getMessage
());
Grasscutter
.
getLogger
().
error
(
"Command error: "
);
e
.
printStackTrace
();
}
}
}
catch
(
Exception
e
)
{
...
...
src/main/java/emu/grasscutter/database/DatabaseHelper.java
View file @
16318b37
...
...
@@ -74,36 +74,36 @@ public class DatabaseHelper {
}
public
static
void
saveAccount
(
Account
account
)
{
DatabaseManager
.
getDatastore
().
save
(
account
);
DatabaseManager
.
get
Account
Datastore
().
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
.
get
Account
Datastore
().
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
.
get
Account
Datastore
().
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
.
get
Account
Datastore
().
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
.
get
Account
Datastore
().
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
.
get
Account
Datastore
().
createQuery
(
Account
.
class
).
field
(
"username"
).
equalIgnoreCase
(
username
);
return
DatabaseManager
.
getDatastore
().
findAndDelete
(
q
)
!=
null
;
}
...
...
src/main/java/emu/grasscutter/database/DatabaseManager.java
View file @
16318b37
...
...
@@ -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
)
{
...
...
src/main/java/emu/grasscutter/game/GenshinPlayer.java
View file @
16318b37
...
...
@@ -293,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
...
...
src/main/java/emu/grasscutter/game/TeamInfo.java
View file @
16318b37
...
...
@@ -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
().
get
Game
ServerOptions
().
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
().
get
Game
ServerOptions
().
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
().
get
Game
ServerOptions
().
MaxAvatarsInTeam
);
}
public
void
copyFrom
(
TeamInfo
team
,
int
maxTeamSize
)
{
...
...
src/main/java/emu/grasscutter/game/TeamManager.java
View file @
16318b37
...
...
@@ -164,13 +164,13 @@ public class TeamManager {
public
int
getMaxTeamSize
()
{
if
(
getPlayer
().
isInMultiplayer
())
{
int
max
=
Grasscutter
.
getConfig
().
getServerOptions
().
MaxAvatarsInTeamMultiplayer
;
int
max
=
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
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
().
get
Game
ServerOptions
().
MaxAvatarsInTeam
;
}
// Methods
...
...
src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
View file @
16318b37
...
...
@@ -92,7 +92,7 @@ public class GachaBanner {
}
public
GachaInfo
toProto
()
{
String
record
=
"http://"
+
(
Grasscutter
.
getConfig
().
Dispatch
Server
PublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
Dispatch
Server
Ip
:
Grasscutter
.
getConfig
().
Dispatch
Server
PublicIp
)
+
"/gacha"
;
String
record
=
"http://"
+
(
Grasscutter
.
getConfig
().
get
Dispatch
Options
().
PublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
get
Dispatch
Options
().
Ip
:
Grasscutter
.
getConfig
().
get
Dispatch
Options
().
PublicIp
)
+
"/gacha"
;
GachaInfo
.
Builder
info
=
GachaInfo
.
newBuilder
()
.
setGachaType
(
this
.
getGachaType
())
...
...
src/main/java/emu/grasscutter/game/gacha/GachaManager.java
View file @
16318b37
...
...
@@ -299,7 +299,7 @@ public class GachaManager {
@Subscribe
public
synchronized
void
watchBannerJson
(
GameServerTickEvent
tickEvent
)
{
if
(
Grasscutter
.
getConfig
().
getServerOptions
().
WatchGacha
)
{
if
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
WatchGacha
)
{
try
{
WatchKey
watchKey
=
watchService
.
take
();
...
...
src/main/java/emu/grasscutter/game/inventory/Inventory.java
View file @
16318b37
...
...
@@ -37,10 +37,10 @@ public class Inventory implements Iterable<GenshinItem> {
this
.
store
=
new
Long2ObjectOpenHashMap
<>();
this
.
inventoryTypes
=
new
Int2ObjectOpenHashMap
<>();
this
.
createInventoryTab
(
ItemType
.
ITEM_WEAPON
,
new
EquipInventoryTab
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitWeapon
));
this
.
createInventoryTab
(
ItemType
.
ITEM_RELIQUARY
,
new
EquipInventoryTab
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitRelic
));
this
.
createInventoryTab
(
ItemType
.
ITEM_MATERIAL
,
new
MaterialInventoryTab
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitMaterial
));
this
.
createInventoryTab
(
ItemType
.
ITEM_FURNITURE
,
new
MaterialInventoryTab
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitFurniture
));
this
.
createInventoryTab
(
ItemType
.
ITEM_WEAPON
,
new
EquipInventoryTab
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitWeapon
));
this
.
createInventoryTab
(
ItemType
.
ITEM_RELIQUARY
,
new
EquipInventoryTab
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitRelic
));
this
.
createInventoryTab
(
ItemType
.
ITEM_MATERIAL
,
new
MaterialInventoryTab
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitMaterial
));
this
.
createInventoryTab
(
ItemType
.
ITEM_FURNITURE
,
new
MaterialInventoryTab
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitFurniture
));
}
public
GenshinPlayer
getPlayer
()
{
...
...
src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java
View file @
16318b37
...
...
@@ -7,6 +7,8 @@ import com.sun.net.httpserver.HttpExchange;
import
com.sun.net.httpserver.HttpServer
;
import
com.sun.net.httpserver.HttpsConfigurator
;
import
com.sun.net.httpserver.HttpsServer
;
import
emu.grasscutter.Config
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.game.Account
;
...
...
@@ -26,50 +28,56 @@ import java.net.InetSocketAddress;
import
java.net.URI
;
import
java.net.URLDecoder
;
import
java.security.KeyStore
;
import
java.util.Base64
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.*
;
public
final
class
DispatchServer
{
public
static
String
query_region_list
=
""
;
public
static
String
query_cur_region
=
""
;
private
final
InetSocketAddress
address
;
private
final
Gson
gson
;
private
final
String
defaultServerName
=
"os_usa"
;
public
String
regionListBase64
;
public
String
regionCurrentBase64
;
private
QueryCurrRegionHttpRsp
currRegion
;
public
HashMap
<
String
,
RegionData
>
regions
;
public
DispatchServer
()
{
this
.
address
=
new
InetSocketAddress
(
Grasscutter
.
getConfig
().
DispatchServerIp
,
Grasscutter
.
getConfig
().
DispatchServerPort
);
this
.
regions
=
new
HashMap
<
String
,
RegionData
>();
this
.
address
=
new
InetSocketAddress
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
Ip
,
Grasscutter
.
getConfig
().
getDispatchOptions
().
Port
);
this
.
gson
=
new
GsonBuilder
().
create
();
this
.
loadQueries
();
this
.
initRegion
();
}
public
InetSocketAddress
getAddress
()
{
return
address
;
}
public
Gson
getGsonFactory
()
{
return
gson
;
}
public
QueryCurrRegionHttpRsp
getCurrRegion
()
{
return
currRegion
;
}
// Needs to be fixed by having the game servers connect to the dispatch server.
if
(
Grasscutter
.
getConfig
().
RunMode
.
equalsIgnoreCase
(
"HYBRID"
))
{
return
regions
.
get
(
defaultServerName
).
parsedRegionQuery
;
}
Grasscutter
.
getLogger
().
warn
(
"[Dispatch] Unsupported run mode for getCurrRegion()"
);
return
null
;
}
public
void
loadQueries
()
{
File
file
;
file
=
new
File
(
Grasscutter
.
getConfig
().
DATA_FOLDER
+
"query_region_list.txt"
);
if
(
file
.
exists
())
{
query_region_list
=
new
String
(
FileUtils
.
read
(
file
));
}
else
{
Grasscutter
.
getLogger
().
warn
(
"[Dispatch] query_region_list not found! Using default region list."
);
}
file
=
new
File
(
Grasscutter
.
getConfig
().
DATA_FOLDER
+
"query_cur_region.txt"
);
if
(
file
.
exists
())
{
query_cur_region
=
new
String
(
FileUtils
.
read
(
file
));
...
...
@@ -82,43 +90,70 @@ public final class DispatchServer {
try
{
byte
[]
decoded
=
Base64
.
getDecoder
().
decode
(
query_region_list
);
QueryRegionListHttpRsp
rl
=
QueryRegionListHttpRsp
.
parseFrom
(
decoded
);
byte
[]
decoded2
=
Base64
.
getDecoder
().
decode
(
query_cur_region
);
QueryCurrRegionHttpRsp
regionQuery
=
QueryCurrRegionHttpRsp
.
parseFrom
(
decoded2
);
RegionSimpleInfo
server
=
RegionSimpleInfo
.
newBuilder
()
.
setName
(
"os_usa"
)
.
setTitle
(
Grasscutter
.
getConfig
().
GameServerName
)
.
setType
(
"DEV_PUBLIC"
)
.
setDispatchUrl
(
"https://"
+
(
Grasscutter
.
getConfig
().
DispatchServerPublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
DispatchServerIp
:
Grasscutter
.
getConfig
().
DispatchServerPublicIp
)
+
":"
+
getAddress
().
getPort
()
+
"/query_cur_region"
)
.
build
();
RegionSimpleInfo
serverTest2
=
RegionSimpleInfo
.
newBuilder
()
.
setName
(
"os_euro"
)
.
setTitle
(
"Grasscutter"
)
.
setType
(
"DEV_PUBLIC"
)
.
setDispatchUrl
(
"https://"
+
(
Grasscutter
.
getConfig
().
DispatchServerPublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
DispatchServerIp
:
Grasscutter
.
getConfig
().
DispatchServerPublicIp
)
+
":"
+
getAddress
().
getPort
()
+
"/query_cur_region"
)
.
build
();
List
<
RegionSimpleInfo
>
servers
=
new
ArrayList
<
RegionSimpleInfo
>();
List
<
String
>
usedNames
=
new
ArrayList
<
String
>();
// List to check for potential naming conflicts
if
(
Grasscutter
.
getConfig
().
RunMode
.
equalsIgnoreCase
(
"HYBRID"
))
{
// Automatically add the game server if in hybrid mode
RegionSimpleInfo
server
=
RegionSimpleInfo
.
newBuilder
()
.
setName
(
"os_usa"
)
.
setTitle
(
Grasscutter
.
getConfig
().
getGameServerOptions
().
Name
)
.
setType
(
"DEV_PUBLIC"
)
.
setDispatchUrl
(
"https://"
+
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
PublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
getDispatchOptions
().
Ip
:
Grasscutter
.
getConfig
().
getDispatchOptions
().
PublicIp
)
+
":"
+
getAddress
().
getPort
()
+
"/query_cur_region_"
+
defaultServerName
)
.
build
();
usedNames
.
add
(
defaultServerName
);
servers
.
add
(
server
);
RegionInfo
serverRegion
=
regionQuery
.
getRegionInfo
().
toBuilder
()
.
setIp
((
Grasscutter
.
getConfig
().
getGameServerOptions
().
PublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
getGameServerOptions
().
Ip
:
Grasscutter
.
getConfig
().
getGameServerOptions
().
PublicIp
))
.
setPort
(
Grasscutter
.
getConfig
().
getGameServerOptions
().
Port
)
.
setSecretKey
(
ByteString
.
copyFrom
(
FileUtils
.
read
(
Grasscutter
.
getConfig
().
KEY_FOLDER
+
"dispatchSeed.bin"
)))
.
build
();
QueryCurrRegionHttpRsp
parsedRegionQuery
=
regionQuery
.
toBuilder
().
setRegionInfo
(
serverRegion
).
build
();
regions
.
put
(
defaultServerName
,
new
RegionData
(
parsedRegionQuery
,
Base64
.
getEncoder
().
encodeToString
(
parsedRegionQuery
.
toByteString
().
toByteArray
())));
QueryRegionListHttpRsp
regionList
=
QueryRegionListHttpRsp
.
newBuilder
()
.
addServers
(
server
)
.
addServers
(
serverTest2
)
.
setClientSecretKey
(
rl
.
getClientSecretKey
())
.
setClientCustomConfigEncrypted
(
rl
.
getClientCustomConfigEncrypted
())
.
setEnableLoginPc
(
true
)
.
build
();
}
else
{
if
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
getGameServers
().
length
==
0
)
{
Grasscutter
.
getLogger
().
error
(
"[Dispatch] There are no game servers available. Exiting due to unplayable state."
);
System
.
exit
(
1
);
}
}
RegionInfo
currentRegion
=
regionQuery
.
getRegionInfo
().
toBuilder
()
.
setIp
((
Grasscutter
.
getConfig
().
GameServerPublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
GameServerIp
:
Grasscutter
.
getConfig
().
GameServerPublicIp
))
.
setPort
(
Grasscutter
.
getConfig
().
GameServerPort
)
.
setSecretKey
(
ByteString
.
copyFrom
(
FileUtils
.
read
(
Grasscutter
.
getConfig
().
KEY_FOLDER
+
"dispatchSeed.bin"
)))
.
build
();
for
(
Config
.
DispatchServerOptions
.
RegionInfo
regionInfo
:
Grasscutter
.
getConfig
().
getDispatchOptions
().
getGameServers
())
{
if
(
usedNames
.
contains
(
regionInfo
.
Name
))
{
Grasscutter
.
getLogger
().
error
(
"Region name already in use."
);
continue
;
}
RegionSimpleInfo
server
=
RegionSimpleInfo
.
newBuilder
()
.
setName
(
regionInfo
.
Name
)
.
setTitle
(
regionInfo
.
Title
)
.
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
)
.
build
();
usedNames
.
add
(
regionInfo
.
Name
);
servers
.
add
(
server
);
RegionInfo
serverRegion
=
regionQuery
.
getRegionInfo
().
toBuilder
()
.
setIp
(
regionInfo
.
Ip
)
.
setPort
(
regionInfo
.
Port
)
.
setSecretKey
(
ByteString
.
copyFrom
(
FileUtils
.
read
(
Grasscutter
.
getConfig
().
KEY_FOLDER
+
"dispatchSeed.bin"
)))
.
build
();
QueryCurrRegionHttpRsp
parsedRegionQuery
=
regionQuery
.
toBuilder
().
setRegionInfo
(
serverRegion
).
build
();
regions
.
put
(
regionInfo
.
Name
,
new
RegionData
(
parsedRegionQuery
,
Base64
.
getEncoder
().
encodeToString
(
parsedRegionQuery
.
toByteString
().
toByteArray
())));
}
QueryCurrRegionHttpRsp
parsedRegionQuery
=
regionQuery
.
toBuilder
().
setRegionInfo
(
currentRegion
).
build
();
QueryRegionListHttpRsp
regionList
=
QueryRegionListHttpRsp
.
newBuilder
()
.
addAllServers
(
servers
)
.
setClientSecretKey
(
rl
.
getClientSecretKey
())
.
setClientCustomConfigEncrypted
(
rl
.
getClientCustomConfigEncrypted
())
.
setEnableLoginPc
(
true
)
.
build
();
this
.
regionListBase64
=
Base64
.
getEncoder
().
encodeToString
(
regionList
.
toByteString
().
toByteArray
());
this
.
regionCurrentBase64
=
Base64
.
getEncoder
().
encodeToString
(
parsedRegionQuery
.
toByteString
().
toByteArray
());
this
.
currRegion
=
parsedRegionQuery
;
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"[Dispatch] Error while initializing region info!"
,
e
);
}
...
...
@@ -126,24 +161,24 @@ public final class DispatchServer {
public
void
start
()
throws
Exception
{
HttpServer
server
;
if
(
Grasscutter
.
getConfig
().
UseSSL
)
{
if
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
UseSSL
)
{
HttpsServer
httpsServer
;
httpsServer
=
HttpsServer
.
create
(
getAddress
(),
0
);
SSLContext
sslContext
=
SSLContext
.
getInstance
(
"TLS"
);
try
(
FileInputStream
fis
=
new
FileInputStream
(
Grasscutter
.
getConfig
().
Dispatch
Server
KeystorePath
))
{
char
[]
keystorePassword
=
Grasscutter
.
getConfig
().
Dispatch
Server
KeystorePassword
.
toCharArray
();
try
(
FileInputStream
fis
=
new
FileInputStream
(
Grasscutter
.
getConfig
().
get
Dispatch
Options
().
KeystorePath
))
{
char
[]
keystorePassword
=
Grasscutter
.
getConfig
().
get
Dispatch
Options
().
KeystorePassword
.
toCharArray
();
KeyStore
ks
=
KeyStore
.
getInstance
(
"PKCS12"
);
ks
.
load
(
fis
,
keystorePassword
);
KeyManagerFactory
kmf
=
KeyManagerFactory
.
getInstance
(
"SunX509"
);
kmf
.
init
(
ks
,
keystorePassword
);
sslContext
.
init
(
kmf
.
getKeyManagers
(),
null
,
null
);
httpsServer
.
setHttpsConfigurator
(
new
HttpsConfigurator
(
sslContext
));
server
=
httpsServer
;
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
warn
(
"[Dispatch] No SSL cert found! Falling back to HTTP server."
);
Grasscutter
.
getConfig
().
UseSSL
=
false
;
Grasscutter
.
getConfig
().
getDispatchOptions
().
UseSSL
=
false
;
server
=
HttpServer
.
create
(
getAddress
(),
0
);
}
}
else
{
...
...
@@ -151,7 +186,7 @@ public final class DispatchServer {
}
server
.
createContext
(
"/"
,
t
->
responseHTML
(
t
,
"Hello"
));
// Dispatch
server
.
createContext
(
"/query_region_list"
,
t
->
{
// Log
...
...
@@ -159,18 +194,22 @@ public final class DispatchServer {
responseHTML
(
t
,
regionListBase64
);
});
server
.
createContext
(
"/query_cur_region"
,
t
->
{
// Log
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s request: query_cur_region"
,
t
.
getRemoteAddress
()));
// Create a response form the request query parameters
URI
uri
=
t
.
getRequestURI
();
String
response
=
"CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="
;
if
(
uri
.
getQuery
()
!=
null
&&
uri
.
getQuery
().
length
()
>
0
)
{
response
=
regionCurrentBase64
;
}
responseHTML
(
t
,
response
);
});
for
(
String
regionName
:
regions
.
keySet
())
{
server
.
createContext
(
"/query_cur_region_"
+
regionName
,
t
->
{
String
regionCurrentBase64
=
regions
.
get
(
regionName
).
Base64
;
// Log
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"Client %s request: query_cur_region_%s"
,
t
.
getRemoteAddress
(),
regionName
));
// Create a response form the request query parameters
URI
uri
=
t
.
getRequestURI
();
String
response
=
"CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="
;
if
(
uri
.
getQuery
()
!=
null
&&
uri
.
getQuery
().
length
()
>
0
)
{
response
=
regionCurrentBase64
;
}
responseHTML
(
t
,
response
);
});
}
// Login via account
server
.
createContext
(
"/hk4e_global/mdk/shield/api/login"
,
t
->
{
// Get post data
...
...
@@ -178,8 +217,8 @@ public final class DispatchServer {
try
{
String
body
=
Utils
.
toString
(
t
.
getRequestBody
());
requestData
=
getGsonFactory
().
fromJson
(
body
,
LoginAccountRequestJson
.
class
);
}
catch
(
Exception
ignored
)
{
}
}
catch
(
Exception
ignored
)
{
}
// Create response json
if
(
requestData
==
null
)
{
return
;
...
...
@@ -187,14 +226,14 @@ public final class DispatchServer {
LoginResultJson
responseData
=
new
LoginResultJson
();
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s is trying to log in"
,
t
.
getRemoteAddress
()));
// Login
Account
account
=
DatabaseHelper
.
getAccountByName
(
requestData
.
account
);
// Check if account exists, else create a new one.
if
(
account
==
null
)
{
// Account doesnt exist, so we can either auto create it if the config value is set
if
(
Grasscutter
.
getConfig
().
Server
Options
.
AutomaticallyCreateAccounts
)
{
if
(
Grasscutter
.
getConfig
().
getDispatch
Options
()
.
AutomaticallyCreateAccounts
)
{
// This account has been created AUTOMATICALLY. There will be no permissions added.
account
=
DatabaseHelper
.
createAccountWithId
(
requestData
.
account
,
0
);
...
...
@@ -216,7 +255,7 @@ public final class DispatchServer {
responseData
.
message
=
"Username not found."
;
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s failed to log in: Account no found"
,
t
.
getRemoteAddress
()));
}
}
}
else
{
// Account was found, log the player in
responseData
.
message
=
"OK"
;
...
...
@@ -229,7 +268,6 @@ public final class DispatchServer {
responseJSON
(
t
,
responseData
);
});
// Login via token
server
.
createContext
(
"/hk4e_global/mdk/shield/api/verify"
,
t
->
{
// Get post data
...
...
@@ -237,8 +275,8 @@ public final class DispatchServer {
try
{
String
body
=
Utils
.
toString
(
t
.
getRequestBody
());
requestData
=
getGsonFactory
().
fromJson
(
body
,
LoginTokenRequestJson
.
class
);
}
catch
(
Exception
ignored
)
{
}
}
catch
(
Exception
ignored
)
{
}
// Create response json
if
(
requestData
==
null
)
{
return
;
...
...
@@ -248,7 +286,7 @@ public final class DispatchServer {
// Login
Account
account
=
DatabaseHelper
.
getAccountById
(
requestData
.
uid
);
// Test
if
(
account
==
null
||
!
account
.
getSessionKey
().
equals
(
requestData
.
token
))
{
responseData
.
retcode
=
-
111
;
...
...
@@ -273,8 +311,8 @@ public final class DispatchServer {
try
{
String
body
=
Utils
.
toString
(
t
.
getRequestBody
());
requestData
=
getGsonFactory
().
fromJson
(
body
,
ComboTokenReqJson
.
class
);
}
catch
(
Exception
ignored
)
{
}
}
catch
(
Exception
ignored
)
{
}
// Create response json
if
(
requestData
==
null
||
requestData
.
data
==
null
)
{
return
;
...
...
@@ -284,7 +322,7 @@ public final class DispatchServer {
// Login
Account
account
=
DatabaseHelper
.
getAccountById
(
loginData
.
uid
);
// Test
if
(
account
==
null
||
!
account
.
getSessionKey
().
equals
(
loginData
.
token
))
{
responseData
.
retcode
=
-
201
;
...
...
@@ -304,16 +342,16 @@ public final class DispatchServer {
});
// Agreement and Protocol
server
.
createContext
(
// hk4e-sdk-os.hoyoverse.com
"/hk4e_global/mdk/agreement/api/getAgreementInfos"
,
"/hk4e_global/mdk/agreement/api/getAgreementInfos"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}"
)
);
server
.
createContext
(
// hk4e-sdk-os.hoyoverse.com
"/hk4e_global/combo/granter/api/compareProtocolVersion"
,
"/hk4e_global/combo/granter/api/compareProtocolVersion"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}"
)
);
// Game data
server
.
createContext
(
// hk4e-api-os.hoyoverse.com
"/common/hk4e_global/announcement/api/getAlertPic"
,
"/common/hk4e_global/announcement/api/getAlertPic"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}"
)
);
server
.
createContext
(
// hk4e-api-os.hoyoverse.com
...
...
@@ -321,54 +359,54 @@ public final class DispatchServer {
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}"
)
);
server
.
createContext
(
// hk4e-api-os.hoyoverse.com
"/common/hk4e_global/announcement/api/getAnnList"
,
"/common/hk4e_global/announcement/api/getAnnList"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0,\"type_list\":[],\"alert\":false,\"alert_id\":0,\"timezone\":0,\"t\":\""
+
System
.
currentTimeMillis
()
+
"\"}}"
)
);
server
.
createContext
(
// hk4e-api-os-static.hoyoverse.com
"/common/hk4e_global/announcement/api/getAnnContent"
,
"/common/hk4e_global/announcement/api/getAnnContent"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0}}"
)
);
server
.
createContext
(
// hk4e-sdk-os.hoyoverse.com
"/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier"
,
"/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}"
)
);
// Captcha
server
.
createContext
(
// api-account-os.hoyoverse.com
"/account/risky/api/check"
,
"/account/risky/api/check"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"c8820f246a5241ab9973f71df3ddd791\",\"action\":\"\",\"geetest\":{\"challenge\":\"\",\"gt\":\"\",\"new_captcha\":0,\"success\":1}}}"
)
);
// Config
server
.
createContext
(
// sdk-os-static.hoyoverse.com
"/combo/box/api/config/sdk/combo"
,
"/combo/box/api/config/sdk/combo"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}"
)
);
server
.
createContext
(
// hk4e-sdk-os-static.hoyoverse.com
"/hk4e_global/combo/granter/api/getConfig"
,
"/hk4e_global/combo/granter/api/getConfig"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\\u0026sdk_screen_transparent=true\\u0026game_biz=hk4e_global\\u0026auth_appid=announcement\\u0026game=hk4e#/\",\"push_alias_type\":2,\"disable_ysdk_guard\":false,\"enable_announce_pic_popup\":true}}"
)
);
server
.
createContext
(
// hk4e-sdk-os-static.hoyoverse.com
"/hk4e_global/mdk/shield/api/loadConfig"
,
"/hk4e_global/mdk/shield/api/loadConfig"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":6,\"game_key\":\"hk4e_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"原神海外\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{\"tw\":\"\",\"fb\":\"\"},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}}}}"
)
);
// Test api?
server
.
createContext
(
// abtest-api-data-sg.hoyoverse.com
"/data_abtest_api/config/experiment/list"
,
"/data_abtest_api/config/experiment/list"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}"
)
);
// Log Server
server
.
createContext
(
// log-upload-os.mihoyo.com
"/log/sdk/upload"
,
"/log/sdk/upload"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
server
.
createContext
(
// log-upload-os.mihoyo.com
"/sdk/upload"
,
"/sdk/upload"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
server
.
createContext
(
// /perf/config/verify?device_id=xxx&platform=x&name=xxx
"/perf/config/verify"
,
"/perf/config/verify"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
// Logging servers
server
.
createContext
(
// overseauspider.yuanshen.com
"/log"
,
...
...
@@ -380,6 +418,7 @@ public final class DispatchServer {
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
server
.
createContext
(
"/gacha"
,
t
->
responseHTML
(
t
,
"<!doctype html><html lang=\"en\"><head><title>Gacha</title></head><body></body></html>"
));
// Start server
server
.
start
();
Grasscutter
.
getLogger
().
info
(
"[Dispatch] Dispatch server started on port "
+
getAddress
().
getPort
());
...
...
@@ -406,7 +445,7 @@ public final class DispatchServer {
os
.
write
(
response
.
getBytes
());
os
.
close
();
}
private
Map
<
String
,
String
>
parseQueryString
(
String
qs
)
{
Map
<
String
,
String
>
result
=
new
HashMap
<>();
if
(
qs
==
null
)
{
...
...
@@ -436,4 +475,15 @@ public final class DispatchServer {
}
return
result
;
}
public
static
class
RegionData
{
QueryCurrRegionHttpRsp
parsedRegionQuery
;
String
Base64
;
public
RegionData
(
QueryCurrRegionHttpRsp
prq
,
String
b64
)
{
this
.
parsedRegionQuery
=
prq
;
this
.
Base64
=
b64
;
}
}
}
src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java
View file @
16318b37
...
...
@@ -87,7 +87,7 @@ public class GameServerPacketHandler {
}
// Log unhandled packets
if
(
Grasscutter
.
getConfig
().
LOG_PACKETS
)
{
if
(
Grasscutter
.
getConfig
().
getGameServerOptions
().
LOG_PACKETS
)
{
//Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + PacketOpcodesUtil.getOpcodeName(opcode));
}
}
...
...
src/main/java/emu/grasscutter/server/game/GameSession.java
View file @
16318b37
...
...
@@ -165,7 +165,7 @@ public class GameSession extends MihoyoKcpChannel {
byte
[]
data
=
genshinPacket
.
build
();
// Log
if
(
Grasscutter
.
getConfig
().
LOG_PACKETS
)
{
if
(
Grasscutter
.
getConfig
().
getGameServerOptions
().
LOG_PACKETS
)
{
logPacket
(
genshinPacket
);
}
...
...
@@ -225,7 +225,7 @@ public class GameSession extends MihoyoKcpChannel {
}
// Log packet
if
(
Grasscutter
.
getConfig
().
LOG_PACKETS
)
{
if
(
Grasscutter
.
getConfig
().
getGameServerOptions
().
LOG_PACKETS
)
{
Grasscutter
.
getLogger
().
info
(
"RECV: "
+
PacketOpcodesUtil
.
getOpcodeName
(
opcode
)
+
" ("
+
opcode
+
")"
);
System
.
out
.
println
(
Utils
.
bytesToHex
(
payload
));
}
...
...
src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java
View file @
16318b37
package
emu.grasscutter.server.packet.send
;
import
com.google.protobuf.ByteString
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.net.packet.GenshinPacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.PlayerLoginRspOuterClass.PlayerLoginRsp
;
import
emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass
;
import
emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.utils.FileUtils
;
import
java.io.File
;
import
java.net.URL
;
import
java.util.Base64
;
public
class
PacketPlayerLoginRsp
extends
GenshinPacket
{
private
static
QueryCurrRegionHttpRspOuterClass
.
QueryCurrRegionHttpRsp
regionCache
;
public
PacketPlayerLoginRsp
(
GameSession
session
)
{
super
(
PacketOpcodes
.
PlayerLoginRsp
,
1
);
this
.
setUseDispatchKey
(
true
);
RegionInfo
info
=
Grasscutter
.
getDispatchServer
().
getCurrRegion
().
getRegionInfo
();
RegionInfo
info
;
if
(
Grasscutter
.
getConfig
().
RunMode
.
equalsIgnoreCase
(
"GAME_ONLY"
))
{
if
(
regionCache
==
null
)
{
try
{
File
file
=
new
File
(
Grasscutter
.
getConfig
().
DATA_FOLDER
+
"query_cur_region.txt"
);
String
query_cur_region
=
""
;
if
(
file
.
exists
())
{
query_cur_region
=
new
String
(
FileUtils
.
read
(
file
));
}
else
{
Grasscutter
.
getLogger
().
warn
(
"query_cur_region not found! Using default current region."
);
}
byte
[]
decodedCurRegion
=
Base64
.
getDecoder
().
decode
(
query_cur_region
);
QueryCurrRegionHttpRspOuterClass
.
QueryCurrRegionHttpRsp
regionQuery
=
QueryCurrRegionHttpRspOuterClass
.
QueryCurrRegionHttpRsp
.
parseFrom
(
decodedCurRegion
);
RegionInfo
serverRegion
=
regionQuery
.
getRegionInfo
().
toBuilder
()
.
setIp
((
Grasscutter
.
getConfig
().
getGameServerOptions
().
PublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
getGameServerOptions
().
Ip
:
Grasscutter
.
getConfig
().
getGameServerOptions
().
PublicIp
))
.
setPort
(
Grasscutter
.
getConfig
().
getGameServerOptions
().
Port
)
.
setSecretKey
(
ByteString
.
copyFrom
(
FileUtils
.
read
(
Grasscutter
.
getConfig
().
KEY_FOLDER
+
"dispatchSeed.bin"
)))
.
build
();
regionCache
=
regionQuery
.
toBuilder
().
setRegionInfo
(
serverRegion
).
build
();
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"Error while initializing region cache!"
,
e
);
}
}
info
=
regionCache
.
getRegionInfo
();
}
else
{
info
=
Grasscutter
.
getDispatchServer
().
getCurrRegion
().
getRegionInfo
();
}
PlayerLoginRsp
p
=
PlayerLoginRsp
.
newBuilder
()
.
setIsUseAbilityHash
(
true
)
// true
.
setAbilityHashCode
(
1844674
)
// 1844674
...
...
src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java
View file @
16318b37
...
...
@@ -19,7 +19,7 @@ public class PacketPlayerStoreNotify extends GenshinPacket {
PlayerStoreNotify
.
Builder
p
=
PlayerStoreNotify
.
newBuilder
()
.
setStoreType
(
StoreType
.
StorePack
)
.
setWeightLimit
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitAll
);
.
setWeightLimit
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitAll
);
for
(
GenshinItem
item
:
player
.
getInventory
())
{
Item
itemProto
=
item
.
toProto
();
...
...
src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java
View file @
16318b37
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.Config.ServerOptions
;
import
emu.grasscutter.Config.
Game
ServerOptions
;
import
emu.grasscutter.GenshinConstants
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.game.GenshinPlayer
;
...
...
@@ -14,7 +14,7 @@ public class PacketPullRecentChatRsp extends GenshinPacket {
public
PacketPullRecentChatRsp
(
GenshinPlayer
player
)
{
super
(
PacketOpcodes
.
PullRecentChatRsp
);
ServerOptions
serverOptions
=
Grasscutter
.
getConfig
().
getServerOptions
();
Game
ServerOptions
serverOptions
=
Grasscutter
.
getConfig
().
get
Game
ServerOptions
();
PullRecentChatRsp
.
Builder
proto
=
PullRecentChatRsp
.
newBuilder
();
if
(
serverOptions
.
WelcomeEmotes
!=
null
&&
serverOptions
.
WelcomeEmotes
.
length
>
0
)
{
...
...
@@ -33,7 +33,7 @@ public class PacketPullRecentChatRsp extends GenshinPacket {
.
setTime
((
int
)
(
System
.
currentTimeMillis
()
/
1000
))
.
setUid
(
GenshinConstants
.
SERVER_CONSOLE_UID
)
.
setToUid
(
player
.
getUid
())
.
setText
(
Grasscutter
.
getConfig
().
getServerOptions
().
WelcomeMotd
)
.
setText
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
WelcomeMotd
)
.
build
();
proto
.
addChatInfo
(
welcomeMotd
);
...
...
src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java
View file @
16318b37
...
...
@@ -13,11 +13,11 @@ public class PacketStoreWeightLimitNotify extends GenshinPacket {
StoreWeightLimitNotify
p
=
StoreWeightLimitNotify
.
newBuilder
()
.
setStoreType
(
StoreType
.
StorePack
)
.
setWeightLimit
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitAll
)
.
setWeaponCountLimit
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitWeapon
)
.
setReliquaryCountLimit
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitRelic
)
.
setMaterialCountLimit
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitMaterial
)
.
setFurnitureCountLimit
(
Grasscutter
.
getConfig
().
getServerOptions
().
InventoryLimitFurniture
)
.
setWeightLimit
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitAll
)
.
setWeaponCountLimit
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitWeapon
)
.
setReliquaryCountLimit
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitRelic
)
.
setMaterialCountLimit
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitMaterial
)
.
setFurnitureCountLimit
(
Grasscutter
.
getConfig
().
get
Game
ServerOptions
().
InventoryLimitFurniture
)
.
build
();
this
.
setData
(
p
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment