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
6554c37d
Commit
6554c37d
authored
Apr 23, 2022
by
WangYneos
Committed by
GitHub
Apr 23, 2022
Browse files
Merge branch 'development' into stable
parents
39e590df
df1dca8d
Changes
39
Show whitespace changes
Inline
Side-by-side
.gitignore
View file @
6554c37d
...
@@ -47,6 +47,7 @@ tmp/
...
@@ -47,6 +47,7 @@ tmp/
# Grasscutter
# Grasscutter
resources/*
resources/*
logs/*
data/AbilityEmbryos.json
data/AbilityEmbryos.json
data/OpenConfig.json
data/OpenConfig.json
proto/auto/
proto/auto/
...
...
README.md
View file @
6554c37d
...
@@ -16,7 +16,7 @@ A WIP server reimplementation for *some anime game* 2.3-2.6
...
@@ -16,7 +16,7 @@ A WIP server reimplementation for *some anime game* 2.3-2.6
*
If you update from an older version, delete
`config.json`
for regeneration
*
If you update from an older version, delete
`config.json`
for regeneration
### Prerequisites
### Prerequisites
*
J
DK-8u202 (
[
mirror link
](
https://mirrors.huaweicloud.com/java/jdk/8u202-b08/
)
since Oracle required an account to download old builds)
*
J
ava 16
*
Mongodb (recommended 4.0+)
*
Mongodb (recommended 4.0+)
*
Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc.
*
Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc.
...
...
build.gradle
View file @
6554c37d
...
@@ -14,12 +14,11 @@ plugins {
...
@@ -14,12 +14,11 @@ plugins {
id
'application'
id
'application'
}
}
sourceCompatibility
=
1
.8
sourceCompatibility
=
1
6
targetCompatibility
=
1
.8
targetCompatibility
=
1
6
repositories
{
repositories
{
mavenCentral
()
mavenCentral
()
jcenter
()
}
}
dependencies
{
dependencies
{
...
@@ -33,9 +32,9 @@ dependencies {
...
@@ -33,9 +32,9 @@ dependencies {
implementation
group:
'com.google.code.gson'
,
name:
'gson'
,
version:
'2.8.8'
implementation
group:
'com.google.code.gson'
,
name:
'gson'
,
version:
'2.8.8'
implementation
group:
'com.google.protobuf'
,
name:
'protobuf-java'
,
version:
'3.18.1'
implementation
group:
'com.google.protobuf'
,
name:
'protobuf-java'
,
version:
'3.18.1'
implementation
group:
'org.reflections'
,
name:
'reflections'
,
version:
'0.
9.1
2'
implementation
group:
'org.reflections'
,
name:
'reflections'
,
version:
'0.
10.
2'
implementation
group:
'dev.morphia.morphia'
,
name:
'core'
,
version:
'
1.6.1
'
implementation
group:
'dev.morphia.morphia'
,
name:
'
morphia-
core'
,
version:
'
2.2.6
'
implementation
group:
'org.greenrobot'
,
name:
'eventbus-java'
,
version:
'3.3.1'
implementation
group:
'org.greenrobot'
,
name:
'eventbus-java'
,
version:
'3.3.1'
}
}
...
...
keystore.p12
View file @
6554c37d
No preview for this file type
proxy.py
View file @
6554c37d
...
@@ -57,7 +57,9 @@ class MlgmXyysd_Genshin_Impact_Proxy:
...
@@ -57,7 +57,9 @@ class MlgmXyysd_Genshin_Impact_Proxy:
"minor-api.mihoyo.com"
,
"minor-api.mihoyo.com"
,
"public-data-api.mihoyo.com"
,
"public-data-api.mihoyo.com"
,
"uspider.yuanshen.com"
,
"uspider.yuanshen.com"
,
"sdk-static.mihoyo.com"
"sdk-static.mihoyo.com"
,
"abtest-api-data-sg.hoyoverse.com"
,
"log-upload-os.hoyoverse.com"
]
]
def
request
(
self
,
flow
:
http
.
HTTPFlow
)
->
None
:
def
request
(
self
,
flow
:
http
.
HTTPFlow
)
->
None
:
...
...
src/main/java/emu/grasscutter/Config.java
View file @
6554c37d
...
@@ -27,9 +27,11 @@ public final class Config {
...
@@ -27,9 +27,11 @@ public final class Config {
public
String
Ip
=
"0.0.0.0"
;
public
String
Ip
=
"0.0.0.0"
;
public
String
PublicIp
=
"127.0.0.1"
;
public
String
PublicIp
=
"127.0.0.1"
;
public
int
Port
=
443
;
public
int
Port
=
443
;
public
int
PublicPort
=
0
;
public
String
KeystorePath
=
"./keystore.p12"
;
public
String
KeystorePath
=
"./keystore.p12"
;
public
String
KeystorePassword
=
""
;
public
String
KeystorePassword
=
"
123456
"
;
public
Boolean
UseSSL
=
true
;
public
Boolean
UseSSL
=
true
;
public
Boolean
FrontHTTPS
=
true
;
public
boolean
AutomaticallyCreateAccounts
=
false
;
public
boolean
AutomaticallyCreateAccounts
=
false
;
...
@@ -52,6 +54,7 @@ public final class Config {
...
@@ -52,6 +54,7 @@ public final class Config {
public
String
Ip
=
"0.0.0.0"
;
public
String
Ip
=
"0.0.0.0"
;
public
String
PublicIp
=
"127.0.0.1"
;
public
String
PublicIp
=
"127.0.0.1"
;
public
int
Port
=
22102
;
public
int
Port
=
22102
;
public
int
PublicPort
=
0
;
public
String
DispatchServerDatabaseUrl
=
"mongodb://localhost:27017"
;
public
String
DispatchServerDatabaseUrl
=
"mongodb://localhost:27017"
;
public
String
DispatchServerDatabaseCollection
=
"grasscutter"
;
public
String
DispatchServerDatabaseCollection
=
"grasscutter"
;
...
...
src/main/java/emu/grasscutter/Grasscutter.java
View file @
6554c37d
...
@@ -34,7 +34,7 @@ public final class Grasscutter {
...
@@ -34,7 +34,7 @@ public final class Grasscutter {
private
static
DispatchServer
dispatchServer
;
private
static
DispatchServer
dispatchServer
;
private
static
GameServer
gameServer
;
private
static
GameServer
gameServer
;
public
static
final
Reflections
reflector
=
new
Reflections
();
public
static
final
Reflections
reflector
=
new
Reflections
(
"emu.grasscutter"
);
static
{
static
{
// Declare logback configuration.
// Declare logback configuration.
...
...
src/main/java/emu/grasscutter/command/CommandMap.java
View file @
6554c37d
...
@@ -11,6 +11,7 @@ import java.util.*;
...
@@ -11,6 +11,7 @@ import java.util.*;
public
final
class
CommandMap
{
public
final
class
CommandMap
{
private
final
Map
<
String
,
CommandHandler
>
commands
=
new
HashMap
<>();
private
final
Map
<
String
,
CommandHandler
>
commands
=
new
HashMap
<>();
private
final
Map
<
String
,
Command
>
annotations
=
new
HashMap
<>();
private
final
Map
<
String
,
Command
>
annotations
=
new
HashMap
<>();
public
CommandMap
()
{
public
CommandMap
()
{
this
(
false
);
this
(
false
);
}
}
...
...
src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java
0 → 100644
View file @
6554c37d
package
emu.grasscutter.command.commands
;
import
emu.grasscutter.command.Command
;
import
emu.grasscutter.command.CommandHandler
;
import
emu.grasscutter.game.GenshinPlayer
;
import
emu.grasscutter.game.inventory.Inventory
;
import
emu.grasscutter.game.inventory.ItemType
;
import
java.util.List
;
@Command
(
label
=
"clearweapons"
,
usage
=
"clearweapons"
,
description
=
"Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory"
,
aliases
=
{
"clearwpns"
},
permission
=
"player.clearweapons"
)
public
final
class
ClearWeaponsCommand
implements
CommandHandler
{
@Override
public
void
execute
(
GenshinPlayer
sender
,
List
<
String
>
args
)
{
if
(
sender
==
null
)
{
CommandHandler
.
sendMessage
(
null
,
"Run this command in-game."
);
return
;
// TODO: clear player's weapons from console or other players
}
Inventory
playerInventory
=
sender
.
getInventory
();
playerInventory
.
getItems
().
values
().
stream
()
.
filter
(
item
->
item
.
getItemType
()
==
ItemType
.
ITEM_WEAPON
)
.
filter
(
item
->
!
item
.
isLocked
()
&&
!
item
.
isEquipped
())
.
forEach
(
item
->
playerInventory
.
removeItem
(
item
,
item
.
getCount
()));
}
}
src/main/java/emu/grasscutter/command/commands/TalentCommand.java
0 → 100644
View file @
6554c37d
package
emu.grasscutter.command.commands
;
import
emu.grasscutter.command.Command
;
import
emu.grasscutter.command.CommandHandler
;
import
emu.grasscutter.game.GenshinPlayer
;
import
emu.grasscutter.game.avatar.GenshinAvatar
;
import
emu.grasscutter.game.entity.EntityAvatar
;
import
emu.grasscutter.server.packet.send.PacketAvatarSkillChangeNotify
;
import
emu.grasscutter.server.packet.send.PacketAvatarSkillUpgradeRsp
;
import
java.util.List
;
@Command
(
label
=
"talent"
,
usage
=
"talent <talentID> <value>"
,
description
=
"Set talent level for your current active character"
,
permission
=
"player.settalent"
)
public
class
TalentCommand
implements
CommandHandler
{
@Override
public
void
execute
(
GenshinPlayer
sender
,
List
<
String
>
args
)
{
if
(
sender
==
null
)
{
CommandHandler
.
sendMessage
(
null
,
"Run this command in-game."
);
return
;
}
if
(
args
.
size
()
<
0
||
args
.
size
()
<
1
){
CommandHandler
.
sendMessage
(
sender
,
"To set talent level: /talent set <talentID> <value>"
);
CommandHandler
.
sendMessage
(
sender
,
"To get talent ID: /talent getid"
);
return
;
}
String
cmdSwitch
=
args
.
get
(
0
);
switch
(
cmdSwitch
)
{
default
:
CommandHandler
.
sendMessage
(
sender
,
"To set talent level: /talent set <talentID> <value>"
);
CommandHandler
.
sendMessage
(
sender
,
"To get talent ID: /talent getid"
);
return
;
case
"set"
:
try
{
int
skillId
=
Integer
.
parseInt
(
args
.
get
(
1
));
int
nextLevel
=
Integer
.
parseInt
(
args
.
get
(
2
));
EntityAvatar
entity
=
sender
.
getTeamManager
().
getCurrentAvatarEntity
();
GenshinAvatar
avatar
=
entity
.
getAvatar
();
int
skillIdNorAtk
=
avatar
.
getData
().
getSkillDepot
().
getSkills
().
get
(
0
);
int
skillIdE
=
avatar
.
getData
().
getSkillDepot
().
getSkills
().
get
(
1
);
int
skillIdQ
=
avatar
.
getData
().
getSkillDepot
().
getEnergySkill
();
int
currentLevelNorAtk
=
avatar
.
getSkillLevelMap
().
get
(
skillIdNorAtk
);
int
currentLevelE
=
avatar
.
getSkillLevelMap
().
get
(
skillIdE
);
int
currentLevelQ
=
avatar
.
getSkillLevelMap
().
get
(
skillIdQ
);
if
(
args
.
size
()
<
2
){
CommandHandler
.
sendMessage
(
sender
,
"To set talent level: /talent set <talentID> <value>"
);
CommandHandler
.
sendMessage
(
sender
,
"To get talent ID: /talent getid"
);
return
;
}
if
(
nextLevel
>
16
){
CommandHandler
.
sendMessage
(
sender
,
"Invalid talent level. Level should be lower than 16"
);
return
;
}
if
(
skillId
==
skillIdNorAtk
){
// Upgrade skill
avatar
.
getSkillLevelMap
().
put
(
skillIdNorAtk
,
nextLevel
);
avatar
.
save
();
// Packet
sender
.
sendPacket
(
new
PacketAvatarSkillChangeNotify
(
avatar
,
skillIdNorAtk
,
currentLevelNorAtk
,
nextLevel
));
sender
.
sendPacket
(
new
PacketAvatarSkillUpgradeRsp
(
avatar
,
skillIdNorAtk
,
currentLevelNorAtk
,
nextLevel
));
CommandHandler
.
sendMessage
(
sender
,
"Set talent Normal ATK to "
+
nextLevel
+
"."
);
}
if
(
skillId
==
skillIdE
){
// Upgrade skill
avatar
.
getSkillLevelMap
().
put
(
skillIdE
,
nextLevel
);
avatar
.
save
();
// Packet
sender
.
sendPacket
(
new
PacketAvatarSkillChangeNotify
(
avatar
,
skillIdE
,
currentLevelE
,
nextLevel
));
sender
.
sendPacket
(
new
PacketAvatarSkillUpgradeRsp
(
avatar
,
skillIdE
,
currentLevelE
,
nextLevel
));
CommandHandler
.
sendMessage
(
sender
,
"Set talent E to "
+
nextLevel
+
"."
);
}
if
(
skillId
==
skillIdQ
){
// Upgrade skill
avatar
.
getSkillLevelMap
().
put
(
skillIdQ
,
nextLevel
);
avatar
.
save
();
// Packet
sender
.
sendPacket
(
new
PacketAvatarSkillChangeNotify
(
avatar
,
skillIdQ
,
currentLevelQ
,
nextLevel
));
sender
.
sendPacket
(
new
PacketAvatarSkillUpgradeRsp
(
avatar
,
skillIdQ
,
currentLevelQ
,
nextLevel
));
CommandHandler
.
sendMessage
(
sender
,
"Set talent Q to "
+
nextLevel
+
"."
);
}
}
catch
(
NumberFormatException
ignored
)
{
CommandHandler
.
sendMessage
(
sender
,
"Invalid skill ID."
);
return
;
}
break
;
case
"getid"
:
EntityAvatar
entity
=
sender
.
getTeamManager
().
getCurrentAvatarEntity
();
GenshinAvatar
avatar
=
entity
.
getAvatar
();
int
skillIdNorAtk
=
avatar
.
getData
().
getSkillDepot
().
getSkills
().
get
(
0
);
int
skillIdE
=
avatar
.
getData
().
getSkillDepot
().
getSkills
().
get
(
1
);
int
skillIdQ
=
avatar
.
getData
().
getSkillDepot
().
getEnergySkill
();
CommandHandler
.
sendMessage
(
sender
,
"Normal Attack ID "
+
skillIdNorAtk
+
"."
);
CommandHandler
.
sendMessage
(
sender
,
"E skill ID "
+
skillIdE
+
"."
);
CommandHandler
.
sendMessage
(
sender
,
"Q skill ID "
+
skillIdQ
+
"."
);
break
;
}
}
}
src/main/java/emu/grasscutter/database/DatabaseCounter.java
View file @
6554c37d
...
@@ -3,7 +3,7 @@ package emu.grasscutter.database;
...
@@ -3,7 +3,7 @@ package emu.grasscutter.database;
import
dev.morphia.annotations.Entity
;
import
dev.morphia.annotations.Entity
;
import
dev.morphia.annotations.Id
;
import
dev.morphia.annotations.Id
;
@Entity
(
value
=
"counters"
,
noClassnameS
tor
ed
=
tru
e
)
@Entity
(
value
=
"counters"
,
useDiscrimina
tor
=
fals
e
)
public
class
DatabaseCounter
{
public
class
DatabaseCounter
{
@Id
@Id
private
String
id
;
private
String
id
;
...
...
src/main/java/emu/grasscutter/database/DatabaseHelper.java
View file @
6554c37d
...
@@ -2,23 +2,16 @@ package emu.grasscutter.database;
...
@@ -2,23 +2,16 @@ package emu.grasscutter.database;
import
java.util.List
;
import
java.util.List
;
import
com.mongodb.WriteResult
;
import
com.mongodb.client.result.DeleteResult
;
import
dev.morphia.query.experimental.filters.Filters
;
import
dev.morphia.query.FindOptions
;
import
dev.morphia.query.Query
;
import
dev.morphia.query.internal.MorphiaCursor
;
import
emu.grasscutter.GenshinConstants
;
import
emu.grasscutter.GenshinConstants
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.game.Account
;
import
emu.grasscutter.game.Account
;
import
emu.grasscutter.game.GenshinPlayer
;
import
emu.grasscutter.game.GenshinPlayer
;
import
emu.grasscutter.game.avatar.GenshinAvatar
;
import
emu.grasscutter.game.avatar.GenshinAvatar
;
import
emu.grasscutter.game.friends.Friendship
;
import
emu.grasscutter.game.friends.Friendship
;
import
emu.grasscutter.game.inventory.GenshinItem
;
import
emu.grasscutter.game.inventory.GenshinItem
;
public
class
DatabaseHelper
{
public
final
class
DatabaseHelper
{
protected
static
FindOptions
FIND_ONE
=
new
FindOptions
().
limit
(
1
);
public
static
Account
createAccount
(
String
username
)
{
public
static
Account
createAccount
(
String
username
)
{
return
createAccountWithId
(
username
,
0
);
return
createAccountWithId
(
username
,
0
);
}
}
...
@@ -36,7 +29,6 @@ public class DatabaseHelper {
...
@@ -36,7 +29,6 @@ public class DatabaseHelper {
if
(
reservedId
==
GenshinConstants
.
SERVER_CONSOLE_UID
)
{
if
(
reservedId
==
GenshinConstants
.
SERVER_CONSOLE_UID
)
{
return
null
;
return
null
;
}
}
exists
=
DatabaseHelper
.
getAccountByPlayerId
(
reservedId
);
exists
=
DatabaseHelper
.
getAccountByPlayerId
(
reservedId
);
if
(
exists
!=
null
)
{
if
(
exists
!=
null
)
{
return
null
;
return
null
;
...
@@ -78,50 +70,37 @@ public class DatabaseHelper {
...
@@ -78,50 +70,37 @@ public class DatabaseHelper {
}
}
public
static
Account
getAccountByName
(
String
username
)
{
public
static
Account
getAccountByName
(
String
username
)
{
MorphiaCursor
<
Account
>
cursor
=
DatabaseManager
.
getAccountDatastore
().
createQuery
(
Account
.
class
).
field
(
"username"
).
equalIgnoreCase
(
username
).
find
(
FIND_ONE
);
return
DatabaseManager
.
getDatastore
().
find
(
Account
.
class
).
filter
(
Filters
.
eq
(
"username"
,
username
)).
first
();
if
(!
cursor
.
hasNext
())
return
null
;
return
cursor
.
next
();
}
}
public
static
Account
getAccountByToken
(
String
token
)
{
public
static
Account
getAccountByToken
(
String
token
)
{
if
(
token
==
null
)
return
null
;
if
(
token
==
null
)
return
null
;
MorphiaCursor
<
Account
>
cursor
=
DatabaseManager
.
getAccountDatastore
().
createQuery
(
Account
.
class
).
field
(
"token"
).
equal
(
token
).
find
(
FIND_ONE
);
return
DatabaseManager
.
getDatastore
().
find
(
Account
.
class
).
filter
(
Filters
.
eq
(
"token"
,
token
)).
first
();
if
(!
cursor
.
hasNext
())
return
null
;
return
cursor
.
next
();
}
}
public
static
Account
getAccountById
(
String
uid
)
{
public
static
Account
getAccountById
(
String
uid
)
{
MorphiaCursor
<
Account
>
cursor
=
DatabaseManager
.
getAccountDatastore
().
createQuery
(
Account
.
class
).
field
(
"_id"
).
equal
(
uid
).
find
(
FIND_ONE
);
return
DatabaseManager
.
getDatastore
().
find
(
Account
.
class
).
filter
(
Filters
.
eq
(
"_id"
,
uid
)).
first
();
if
(!
cursor
.
hasNext
())
return
null
;
return
cursor
.
next
();
}
}
public
static
Account
getAccountByPlayerId
(
int
playerId
)
{
public
static
Account
getAccountByPlayerId
(
int
playerId
)
{
MorphiaCursor
<
Account
>
cursor
=
DatabaseManager
.
getAccountDatastore
().
createQuery
(
Account
.
class
).
field
(
"playerId"
).
equal
(
playerId
).
find
(
FIND_ONE
);
return
DatabaseManager
.
getDatastore
().
find
(
Account
.
class
).
filter
(
Filters
.
eq
(
"playerId"
,
playerId
)).
first
();
if
(!
cursor
.
hasNext
())
return
null
;
return
cursor
.
next
();
}
}
public
static
boolean
deleteAccount
(
String
username
)
{
public
static
boolean
deleteAccount
(
String
username
)
{
Query
<
Account
>
q
=
DatabaseManager
.
getAccountDatastore
().
createQuery
(
Account
.
class
).
field
(
"username"
).
equalIgnoreCase
(
username
);
return
DatabaseManager
.
getDatastore
().
find
(
Account
.
class
).
filter
(
Filters
.
eq
(
"username"
,
username
)).
delete
().
getDeletedCount
()
>
0
;
return
DatabaseManager
.
getDatastore
().
findAndDelete
(
q
)
!=
null
;
}
}
public
static
GenshinPlayer
getPlayerById
(
int
id
)
{
public
static
GenshinPlayer
getPlayerById
(
int
id
)
{
Query
<
GenshinPlayer
>
query
=
DatabaseManager
.
getDatastore
().
createQuery
(
GenshinPlayer
.
class
).
field
(
"_id"
).
equal
(
id
);
return
DatabaseManager
.
getDatastore
().
find
(
GenshinPlayer
.
class
).
filter
(
Filters
.
eq
(
"_id"
,
id
)).
first
();
MorphiaCursor
<
GenshinPlayer
>
cursor
=
query
.
find
(
FIND_ONE
);
if
(!
cursor
.
hasNext
())
return
null
;
return
cursor
.
next
();
}
}
public
static
boolean
checkPlayerExists
(
int
id
)
{
public
static
boolean
checkPlayerExists
(
int
id
)
{
MorphiaCursor
<
GenshinPlayer
>
query
=
DatabaseManager
.
getDatastore
().
createQuery
(
GenshinPlayer
.
class
).
field
(
"_id"
).
equal
(
id
).
find
(
FIND_ONE
);
return
DatabaseManager
.
getDatastore
().
find
(
GenshinPlayer
.
class
).
filter
(
Filters
.
eq
(
"_id"
,
id
)).
first
()
!=
null
;
return
query
.
hasNext
();
}
}
public
static
synchronized
GenshinPlayer
createPlayer
(
GenshinPlayer
character
,
int
reservedId
)
{
public
static
synchronized
GenshinPlayer
createPlayer
(
GenshinPlayer
character
,
int
reservedId
)
{
// Check if reserved id
// Check if reserved id
int
id
=
0
;
int
id
;
if
(
reservedId
>
0
&&
!
checkPlayerExists
(
reservedId
))
{
if
(
reservedId
>
0
&&
!
checkPlayerExists
(
reservedId
))
{
id
=
reservedId
;
id
=
reservedId
;
character
.
setUid
(
id
);
character
.
setUid
(
id
);
...
@@ -139,7 +118,7 @@ public class DatabaseHelper {
...
@@ -139,7 +118,7 @@ public class DatabaseHelper {
public
static
synchronized
int
getNextPlayerId
(
int
reservedId
)
{
public
static
synchronized
int
getNextPlayerId
(
int
reservedId
)
{
// Check if reserved id
// Check if reserved id
int
id
=
0
;
int
id
;
if
(
reservedId
>
0
&&
!
checkPlayerExists
(
reservedId
))
{
if
(
reservedId
>
0
&&
!
checkPlayerExists
(
reservedId
))
{
id
=
reservedId
;
id
=
reservedId
;
}
else
{
}
else
{
...
@@ -160,8 +139,7 @@ public class DatabaseHelper {
...
@@ -160,8 +139,7 @@ public class DatabaseHelper {
}
}
public
static
List
<
GenshinAvatar
>
getAvatars
(
GenshinPlayer
player
)
{
public
static
List
<
GenshinAvatar
>
getAvatars
(
GenshinPlayer
player
)
{
Query
<
GenshinAvatar
>
query
=
DatabaseManager
.
getDatastore
().
createQuery
(
GenshinAvatar
.
class
).
filter
(
"ownerId"
,
player
.
getUid
());
return
DatabaseManager
.
getDatastore
().
find
(
GenshinAvatar
.
class
).
filter
(
Filters
.
eq
(
"ownerId"
,
player
.
getUid
())).
stream
().
toList
();
return
query
.
find
().
toList
();
}
}
public
static
void
saveItem
(
GenshinItem
item
)
{
public
static
void
saveItem
(
GenshinItem
item
)
{
...
@@ -169,22 +147,19 @@ public class DatabaseHelper {
...
@@ -169,22 +147,19 @@ public class DatabaseHelper {
}
}
public
static
boolean
deleteItem
(
GenshinItem
item
)
{
public
static
boolean
deleteItem
(
GenshinItem
item
)
{
Wri
teResult
result
=
DatabaseManager
.
getDatastore
().
delete
(
item
);
Dele
teResult
result
=
DatabaseManager
.
getDatastore
().
delete
(
item
);
return
result
.
wasAcknowledged
();
return
result
.
wasAcknowledged
();
}
}
public
static
List
<
GenshinItem
>
getInventoryItems
(
GenshinPlayer
player
)
{
public
static
List
<
GenshinItem
>
getInventoryItems
(
GenshinPlayer
player
)
{
Query
<
GenshinItem
>
query
=
DatabaseManager
.
getDatastore
().
createQuery
(
GenshinItem
.
class
).
filter
(
"ownerId"
,
player
.
getUid
());
return
DatabaseManager
.
getDatastore
().
find
(
GenshinItem
.
class
).
filter
(
Filters
.
eq
(
"ownerId"
,
player
.
getUid
())).
stream
().
toList
();
return
query
.
find
().
toList
();
}
}
public
static
List
<
Friendship
>
getFriends
(
GenshinPlayer
player
)
{
public
static
List
<
Friendship
>
getFriends
(
GenshinPlayer
player
)
{
Query
<
Friendship
>
query
=
DatabaseManager
.
getDatastore
().
createQuery
(
Friendship
.
class
).
filter
(
"ownerId"
,
player
.
getUid
());
return
DatabaseManager
.
getDatastore
().
find
(
Friendship
.
class
).
filter
(
Filters
.
eq
(
"ownerId"
,
player
.
getUid
())).
stream
().
toList
();
return
query
.
find
().
toList
();
}
}
public
static
List
<
Friendship
>
getReverseFriends
(
GenshinPlayer
player
)
{
public
static
List
<
Friendship
>
getReverseFriends
(
GenshinPlayer
player
)
{
Query
<
Friendship
>
query
=
DatabaseManager
.
getDatastore
().
createQuery
(
Friendship
.
class
).
filter
(
"friendId"
,
player
.
getUid
());
return
DatabaseManager
.
getDatastore
().
find
(
Friendship
.
class
).
filter
(
Filters
.
eq
(
"friendId"
,
player
.
getUid
())).
stream
().
toList
();
return
query
.
find
().
toList
();
}
}
public
static
void
saveFriendship
(
Friendship
friendship
)
{
public
static
void
saveFriendship
(
Friendship
friendship
)
{
...
@@ -196,13 +171,9 @@ public class DatabaseHelper {
...
@@ -196,13 +171,9 @@ public class DatabaseHelper {
}
}
public
static
Friendship
getReverseFriendship
(
Friendship
friendship
)
{
public
static
Friendship
getReverseFriendship
(
Friendship
friendship
)
{
Query
<
Friendship
>
query
=
DatabaseManager
.
getDatastore
().
createQuery
(
Friendship
.
class
);
return
DatabaseManager
.
getDatastore
().
find
(
Friendship
.
class
).
filter
(
Filters
.
and
(
query
.
and
(
Filters
.
eq
(
"ownerId"
,
friendship
.
getFriendId
()),
query
.
criteria
(
"ownerId"
).
equal
(
friendship
.
getFriendId
()),
Filters
.
eq
(
"friendId"
,
friendship
.
getOwnerId
())
query
.
criteria
(
"friendId"
).
equal
(
friendship
.
getOwnerId
())
)).
first
();
);
MorphiaCursor
<
Friendship
>
reverseFriendship
=
query
.
find
(
FIND_ONE
);
if
(!
reverseFriendship
.
hasNext
())
return
null
;
return
reverseFriendship
.
next
();
}
}
}
}
src/main/java/emu/grasscutter/database/DatabaseManager.java
View file @
6554c37d
package
emu.grasscutter.database
;
package
emu.grasscutter.database
;
import
com.mongodb.MongoClient
;
import
com.mongodb.MongoClientURI
;
import
com.mongodb.MongoClientURI
;
import
com.mongodb.MongoCommandException
;
import
com.mongodb.MongoCommandException
;
import
com.mongodb.client.MongoClient
;
import
com.mongodb.client.MongoClients
;
import
com.mongodb.client.MongoDatabase
;
import
com.mongodb.client.MongoDatabase
;
import
com.mongodb.client.MongoIterable
;
import
com.mongodb.client.MongoIterable
;
import
dev.morphia.Datastore
;
import
dev.morphia.Datastore
;
import
dev.morphia.Morphia
;
import
dev.morphia.Morphia
;
import
dev.morphia.mapping.MapperOptions
;
import
dev.morphia.query.experimental.filters.Filters
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.game.Account
;
import
emu.grasscutter.game.Account
;
import
emu.grasscutter.game.GenshinPlayer
;
import
emu.grasscutter.game.GenshinPlayer
;
...
@@ -16,6 +19,7 @@ import emu.grasscutter.game.friends.Friendship;
...
@@ -16,6 +19,7 @@ import emu.grasscutter.game.friends.Friendship;
import
emu.grasscutter.game.inventory.GenshinItem
;
import
emu.grasscutter.game.inventory.GenshinItem
;
public
final
class
DatabaseManager
{
public
final
class
DatabaseManager
{
private
static
MongoClient
mongoClient
;
private
static
MongoClient
mongoClient
;
private
static
MongoClient
dispatchMongoClient
;
private
static
MongoClient
dispatchMongoClient
;
...
@@ -26,10 +30,6 @@ public final class DatabaseManager {
...
@@ -26,10 +30,6 @@ public final class DatabaseManager {
DatabaseCounter
.
class
,
Account
.
class
,
GenshinPlayer
.
class
,
GenshinAvatar
.
class
,
GenshinItem
.
class
,
Friendship
.
class
DatabaseCounter
.
class
,
Account
.
class
,
GenshinPlayer
.
class
,
GenshinAvatar
.
class
,
GenshinItem
.
class
,
Friendship
.
class
};
};
public
static
MongoClient
getMongoClient
()
{
return
mongoClient
;
}
public
static
Datastore
getDatastore
()
{
public
static
Datastore
getDatastore
()
{
return
datastore
;
return
datastore
;
}
}
...
@@ -50,27 +50,23 @@ public final class DatabaseManager {
...
@@ -50,27 +50,23 @@ public final class DatabaseManager {
public
static
void
initialize
()
{
public
static
void
initialize
()
{
// Initialize
// Initialize
mongoClient
=
new
MongoClient
(
new
MongoClientURI
(
Grasscutter
.
getConfig
().
DatabaseUrl
));
MongoClient
mongoClient
=
MongoClients
.
create
(
Grasscutter
.
getConfig
().
DatabaseUrl
);
Morphia
morphia
=
new
Morphia
();
// TODO Update when migrating to Morphia 2.0
morphia
.
getMapper
().
getOptions
().
setStoreEmpties
(
true
);
morphia
.
getMapper
().
getOptions
().
setStoreNulls
(
false
);
morphia
.
getMapper
().
getOptions
().
setDisableEmbeddedIndexes
(
true
);
// Map
// Set mapper options.
morphia
.
map
(
mappedClasses
);
MapperOptions
mapperOptions
=
MapperOptions
.
builder
()
.
storeEmpties
(
true
).
storeNulls
(
false
).
build
();
// Build datastore
// Create data store.
datastore
=
morphia
.
createDatastore
(
mongoClient
,
Grasscutter
.
getConfig
().
DatabaseCollection
);
datastore
=
Morphia
.
createDatastore
(
mongoClient
,
Grasscutter
.
getConfig
().
DatabaseCollection
,
mapperOptions
);
// Map classes.
datastore
.
getMapper
().
map
(
mappedClasses
);
// Ensure indexes
// Ensure indexes
try
{
try
{
datastore
.
ensureIndexes
();
datastore
.
ensureIndexes
();
}
catch
(
MongoCommandException
e
)
{
}
catch
(
MongoCommandException
e
xception
)
{
Grasscutter
.
getLogger
().
info
(
"Mongo index error: "
,
e
);
Grasscutter
.
getLogger
().
info
(
"Mongo index error: "
,
e
xception
);
// Duplicate index error
// Duplicate index error
if
(
e
.
getCode
()
==
85
)
{
if
(
e
xception
.
getCode
()
==
85
)
{
// Drop all indexes and re add them
// Drop all indexes and re add them
MongoIterable
<
String
>
collections
=
datastore
.
getDatabase
().
listCollectionNames
();
MongoIterable
<
String
>
collections
=
datastore
.
getDatabase
().
listCollectionNames
();
for
(
String
name
:
collections
)
{
for
(
String
name
:
collections
)
{
...
@@ -82,8 +78,8 @@ public final class DatabaseManager {
...
@@ -82,8 +78,8 @@ public final class DatabaseManager {
}
}
if
(
Grasscutter
.
getConfig
().
RunMode
.
equalsIgnoreCase
(
"GAME_ONLY"
))
{
if
(
Grasscutter
.
getConfig
().
RunMode
.
equalsIgnoreCase
(
"GAME_ONLY"
))
{
dispatchMongoClient
=
new
MongoClient
(
new
MongoClientURI
(
Grasscutter
.
getConfig
().
getGameServerOptions
().
DispatchServerDatabaseUrl
)
)
;
dispatchMongoClient
=
MongoClient
s
.
create
(
Grasscutter
.
getConfig
().
getGameServerOptions
().
DispatchServerDatabaseUrl
);
dispatchDatastore
=
m
orphia
.
createDatastore
(
dispatchMongoClient
,
Grasscutter
.
getConfig
().
getGameServerOptions
().
DispatchServerDatabaseCollection
);
dispatchDatastore
=
M
orphia
.
createDatastore
(
dispatchMongoClient
,
Grasscutter
.
getConfig
().
getGameServerOptions
().
DispatchServerDatabaseCollection
);
// Ensure indexes for dispatch server
// Ensure indexes for dispatch server
try
{
try
{
...
@@ -105,7 +101,7 @@ public final class DatabaseManager {
...
@@ -105,7 +101,7 @@ public final class DatabaseManager {
}
}
public
static
synchronized
int
getNextId
(
Class
<?>
c
)
{
public
static
synchronized
int
getNextId
(
Class
<?>
c
)
{
DatabaseCounter
counter
=
getDatastore
().
createQuery
(
DatabaseCounter
.
class
).
fi
eld
(
"_id"
).
equal
(
c
.
getSimple
Name
()).
fi
nd
().
tryNex
t
();
DatabaseCounter
counter
=
getDatastore
().
find
(
DatabaseCounter
.
class
).
fi
lter
(
Filters
.
eq
(
"_id"
,
c
.
get
Name
())
)
.
fi
rs
t
();
if
(
counter
==
null
)
{
if
(
counter
==
null
)
{
counter
=
new
DatabaseCounter
(
c
.
getSimpleName
());
counter
=
new
DatabaseCounter
(
c
.
getSimpleName
());
}
}
...
...
src/main/java/emu/grasscutter/game/Account.java
View file @
6554c37d
package
emu.grasscutter.game
;
package
emu.grasscutter.game
;
import
dev.morphia.annotations.AlsoLoad
;
import
dev.morphia.annotations.*
;
import
dev.morphia.annotations.Collation
;
import
dev.morphia.annotations.Entity
;
import
dev.morphia.annotations.Id
;
import
dev.morphia.annotations.Indexed
;
import
dev.morphia.annotations.PreLoad
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.utils.Crypto
;
import
emu.grasscutter.utils.Crypto
;
import
emu.grasscutter.utils.Utils
;
import
emu.grasscutter.utils.Utils
;
import
dev.morphia.annotations.IndexOptions
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
org.bson.Document
;
import
com.mongodb.DBObject
;
import
com.mongodb.DBObject
;
@Entity
(
value
=
"accounts"
,
noClassnameS
tor
ed
=
tru
e
)
@Entity
(
value
=
"accounts"
,
useDiscrimina
tor
=
fals
e
)
public
class
Account
{
public
class
Account
{
@Id
private
String
id
;
@Id
private
String
id
;
...
@@ -122,15 +118,15 @@ public class Account {
...
@@ -122,15 +118,15 @@ public class Account {
return
this
.
token
;
return
this
.
token
;
}
}
public
void
save
()
{
DatabaseHelper
.
saveAccount
(
this
);
}
@PreLoad
@PreLoad
public
void
onLoad
(
D
BObject
dbObj
)
{
public
void
onLoad
(
D
ocument
document
)
{
// Grant the superuser permissions to accounts created before the permissions update
// Grant the superuser permissions to accounts created before the permissions update
if
(!
d
bObj
.
contains
Field
(
"permissions"
))
{
if
(!
d
ocument
.
contains
Key
(
"permissions"
))
{
this
.
addPermission
(
"*"
);
this
.
addPermission
(
"*"
);
}
}
}
}
public
void
save
()
{
DatabaseHelper
.
saveAccount
(
this
);
}
}
}
src/main/java/emu/grasscutter/game/GenshinPlayer.java
View file @
6554c37d
...
@@ -18,6 +18,7 @@ import emu.grasscutter.game.friends.PlayerProfile;
...
@@ -18,6 +18,7 @@ import emu.grasscutter.game.friends.PlayerProfile;
import
emu.grasscutter.game.gacha.PlayerGachaInfo
;
import
emu.grasscutter.game.gacha.PlayerGachaInfo
;
import
emu.grasscutter.game.inventory.GenshinItem
;
import
emu.grasscutter.game.inventory.GenshinItem
;
import
emu.grasscutter.game.inventory.Inventory
;
import
emu.grasscutter.game.inventory.Inventory
;
import
emu.grasscutter.game.player.PlayerBirthday
;
import
emu.grasscutter.game.props.ActionReason
;
import
emu.grasscutter.game.props.ActionReason
;
import
emu.grasscutter.game.props.PlayerProperty
;
import
emu.grasscutter.game.props.PlayerProperty
;
import
emu.grasscutter.net.packet.GenshinPacket
;
import
emu.grasscutter.net.packet.GenshinPacket
;
...
@@ -61,7 +62,7 @@ import emu.grasscutter.utils.Position;
...
@@ -61,7 +62,7 @@ import emu.grasscutter.utils.Position;
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
=
"players"
,
noClassnameS
tor
ed
=
tru
e
)
@Entity
(
value
=
"players"
,
useDiscrimina
tor
=
fals
e
)
public
class
GenshinPlayer
{
public
class
GenshinPlayer
{
@Id
private
int
id
;
@Id
private
int
id
;
@Indexed
(
options
=
@IndexOptions
(
unique
=
true
))
private
String
accountId
;
@Indexed
(
options
=
@IndexOptions
(
unique
=
true
))
private
String
accountId
;
...
@@ -73,6 +74,7 @@ public class GenshinPlayer {
...
@@ -73,6 +74,7 @@ public class GenshinPlayer {
private
int
nameCardId
=
210001
;
private
int
nameCardId
=
210001
;
private
Position
pos
;
private
Position
pos
;
private
Position
rotation
;
private
Position
rotation
;
private
PlayerBirthday
birthday
;
private
Map
<
Integer
,
Integer
>
properties
;
private
Map
<
Integer
,
Integer
>
properties
;
private
Set
<
Integer
>
nameCardList
;
private
Set
<
Integer
>
nameCardList
;
...
@@ -139,6 +141,8 @@ public class GenshinPlayer {
...
@@ -139,6 +141,8 @@ public class GenshinPlayer {
this
.
combatInvokeHandler
=
new
InvokeHandler
(
PacketCombatInvocationsNotify
.
class
);
this
.
combatInvokeHandler
=
new
InvokeHandler
(
PacketCombatInvocationsNotify
.
class
);
this
.
abilityInvokeHandler
=
new
InvokeHandler
(
PacketAbilityInvocationsNotify
.
class
);
this
.
abilityInvokeHandler
=
new
InvokeHandler
(
PacketAbilityInvocationsNotify
.
class
);
this
.
clientAbilityInitFinishHandler
=
new
InvokeHandler
(
PacketClientAbilityInitFinishNotify
.
class
);
this
.
clientAbilityInitFinishHandler
=
new
InvokeHandler
(
PacketClientAbilityInitFinishNotify
.
class
);
this
.
birthday
=
new
PlayerBirthday
();
}
}
// On player creation
// On player creation
...
@@ -150,6 +154,7 @@ public class GenshinPlayer {
...
@@ -150,6 +154,7 @@ public class GenshinPlayer {
this
.
nickname
=
"Traveler"
;
this
.
nickname
=
"Traveler"
;
this
.
signature
=
""
;
this
.
signature
=
""
;
this
.
teamManager
=
new
TeamManager
(
this
);
this
.
teamManager
=
new
TeamManager
(
this
);
this
.
birthday
=
new
PlayerBirthday
();
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_LEVEL
,
1
);
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_LEVEL
,
1
);
this
.
setProperty
(
PlayerProperty
.
PROP_IS_SPRING_AUTO_USE
,
1
);
this
.
setProperty
(
PlayerProperty
.
PROP_IS_SPRING_AUTO_USE
,
1
);
this
.
setProperty
(
PlayerProperty
.
PROP_SPRING_AUTO_USE_PERCENT
,
50
);
this
.
setProperty
(
PlayerProperty
.
PROP_SPRING_AUTO_USE_PERCENT
,
50
);
...
@@ -642,6 +647,15 @@ public class GenshinPlayer {
...
@@ -642,6 +647,15 @@ public class GenshinPlayer {
return
onlineInfo
.
build
();
return
onlineInfo
.
build
();
}
}
public
PlayerBirthday
getBirthday
(){
return
this
.
birthday
;
}
public
void
setBirthday
(
int
d
,
int
m
)
{
this
.
birthday
=
new
PlayerBirthday
(
d
,
m
);
this
.
updateProfile
();
}
public
SocialDetail
.
Builder
getSocialDetail
()
{
public
SocialDetail
.
Builder
getSocialDetail
()
{
SocialDetail
.
Builder
social
=
SocialDetail
.
newBuilder
()
SocialDetail
.
Builder
social
=
SocialDetail
.
newBuilder
()
.
setUid
(
this
.
getUid
())
.
setUid
(
this
.
getUid
())
...
@@ -649,7 +663,7 @@ public class GenshinPlayer {
...
@@ -649,7 +663,7 @@ public class GenshinPlayer {
.
setNickname
(
this
.
getNickname
())
.
setNickname
(
this
.
getNickname
())
.
setSignature
(
this
.
getSignature
())
.
setSignature
(
this
.
getSignature
())
.
setLevel
(
this
.
getLevel
())
.
setLevel
(
this
.
getLevel
())
.
setBirthday
(
Birthday
.
newBuilder
())
.
setBirthday
(
this
.
getBirthday
().
getFilledProtoWhenNotEmpty
())
.
setWorldLevel
(
this
.
getWorldLevel
())
.
setWorldLevel
(
this
.
getWorldLevel
())
.
setUnk1
(
1
)
.
setUnk1
(
1
)
.
setUnk3
(
1
)
.
setUnk3
(
1
)
...
...
src/main/java/emu/grasscutter/game/TeamInfo.java
View file @
6554c37d
...
@@ -3,10 +3,12 @@ package emu.grasscutter.game;
...
@@ -3,10 +3,12 @@ package emu.grasscutter.game;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
dev.morphia.annotations.Entity
;
import
emu.grasscutter.GenshinConstants
;
import
emu.grasscutter.GenshinConstants
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.game.avatar.GenshinAvatar
;
import
emu.grasscutter.game.avatar.GenshinAvatar
;
@Entity
public
class
TeamInfo
{
public
class
TeamInfo
{
private
String
name
;
private
String
name
;
private
List
<
Integer
>
avatars
;
private
List
<
Integer
>
avatars
;
...
...
src/main/java/emu/grasscutter/game/TeamManager.java
View file @
6554c37d
...
@@ -8,6 +8,7 @@ import java.util.List;
...
@@ -8,6 +8,7 @@ import java.util.List;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.Set
;
import
dev.morphia.annotations.Entity
;
import
dev.morphia.annotations.Transient
;
import
dev.morphia.annotations.Transient
;
import
emu.grasscutter.GenshinConstants
;
import
emu.grasscutter.GenshinConstants
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.Grasscutter
;
...
@@ -41,6 +42,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
...
@@ -41,6 +42,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import
it.unimi.dsi.fastutil.ints.IntOpenHashSet
;
import
it.unimi.dsi.fastutil.ints.IntOpenHashSet
;
import
it.unimi.dsi.fastutil.ints.IntSet
;
import
it.unimi.dsi.fastutil.ints.IntSet
;
@Entity
public
class
TeamManager
{
public
class
TeamManager
{
@Transient
private
GenshinPlayer
player
;
@Transient
private
GenshinPlayer
player
;
...
...
src/main/java/emu/grasscutter/game/avatar/AvatarProfileData.java
View file @
6554c37d
package
emu.grasscutter.game.avatar
;
package
emu.grasscutter.game.avatar
;
import
dev.morphia.annotations.Entity
;
@Entity
public
class
AvatarProfileData
{
public
class
AvatarProfileData
{
private
int
avatarId
;
private
int
avatarId
;
private
int
level
;
private
int
level
;
...
...
src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java
View file @
6554c37d
...
@@ -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"
,
noClassnameS
tor
ed
=
tru
e
)
@Entity
(
value
=
"avatars"
,
useDiscrimina
tor
=
fals
e
)
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
...
...
src/main/java/emu/grasscutter/game/friends/Friendship.java
View file @
6554c37d
...
@@ -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"
,
noClassnameS
tor
ed
=
tru
e
)
@Entity
(
value
=
"friendships"
,
useDiscrimina
tor
=
fals
e
)
public
class
Friendship
{
public
class
Friendship
{
@Id
private
ObjectId
id
;
@Id
private
ObjectId
id
;
...
...
Prev
1
2
Next
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