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
dce13cf6
Commit
dce13cf6
authored
Apr 25, 2022
by
Magix
Committed by
GitHub
Apr 25, 2022
Browse files
Merge branch 'development' into plugin-system
parents
832c460a
b6fedcf2
Changes
48
Hide whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java
View file @
dce13cf6
...
...
@@ -197,8 +197,8 @@ public final class SetStatsCommand implements CommandHandler {
float
eelec
=
Integer
.
parseInt
(
args
.
get
(
1
));
EntityAvatar
entity
=
sender
.
getTeamManager
().
getCurrentAvatarEntity
();
float
elec
=
eelec
/
10000
;
entity
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_
CRITICAL
_HURT
,
elec
);
entity
.
getWorld
().
broadcastPacket
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
FightProperty
.
FIGHT_PROP_
CRITICAL
_HURT
));
entity
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_
ELEC_ADD
_HURT
,
elec
);
entity
.
getWorld
().
broadcastPacket
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
FightProperty
.
FIGHT_PROP_
ELEC_ADD
_HURT
));
float
igelec
=
elec
*
100
;
CommandHandler
.
sendMessage
(
sender
,
"Electro DMG Bonus set to "
+
igelec
+
"%"
);
}
catch
(
NumberFormatException
ignored
)
{
...
...
src/main/java/emu/grasscutter/command/commands/TalentCommand.java
View file @
dce13cf6
...
...
@@ -2,6 +2,7 @@ package emu.grasscutter.command.commands;
import
emu.grasscutter.command.Command
;
import
emu.grasscutter.command.CommandHandler
;
import
emu.grasscutter.data.def.AvatarSkillDepotData
;
import
emu.grasscutter.game.GenshinPlayer
;
import
emu.grasscutter.game.avatar.GenshinAvatar
;
import
emu.grasscutter.game.entity.EntityAvatar
;
...
...
@@ -21,8 +22,9 @@ public class TalentCommand implements CommandHandler {
return
;
}
if
(
args
.
size
()
<
0
||
args
.
size
()
<
1
){
if
(
args
.
size
()
<
1
){
CommandHandler
.
sendMessage
(
sender
,
"To set talent level: /talent set <talentID> <value>"
);
CommandHandler
.
sendMessage
(
sender
,
"Another way to set talent level: /talent <n or e or q> <value>"
);
CommandHandler
.
sendMessage
(
sender
,
"To get talent ID: /talent getid"
);
return
;
}
...
...
@@ -31,6 +33,7 @@ public class TalentCommand implements CommandHandler {
switch
(
cmdSwitch
)
{
default
:
CommandHandler
.
sendMessage
(
sender
,
"To set talent level: /talent set <talentID> <value>"
);
CommandHandler
.
sendMessage
(
sender
,
"Another way to set talent level: /talent <n or e or q> <value>"
);
CommandHandler
.
sendMessage
(
sender
,
"To get talent ID: /talent getid"
);
return
;
case
"set"
:
...
...
@@ -90,6 +93,45 @@ public class TalentCommand implements CommandHandler {
return
;
}
break
;
case
"n"
:
case
"e"
:
case
"q"
:
try
{
EntityAvatar
entity
=
sender
.
getTeamManager
().
getCurrentAvatarEntity
();
GenshinAvatar
avatar
=
entity
.
getAvatar
();
AvatarSkillDepotData
SkillDepot
=
avatar
.
getData
().
getSkillDepot
();
int
skillId
;
switch
(
cmdSwitch
)
{
default
:
skillId
=
SkillDepot
.
getSkills
().
get
(
0
);
break
;
case
"e"
:
skillId
=
SkillDepot
.
getSkills
().
get
(
1
);
break
;
case
"q"
:
skillId
=
SkillDepot
.
getEnergySkill
();
break
;
}
int
nextLevel
=
Integer
.
parseInt
(
args
.
get
(
1
));
int
currentLevel
=
avatar
.
getSkillLevelMap
().
get
(
skillId
);
if
(
args
.
size
()
<
1
){
CommandHandler
.
sendMessage
(
sender
,
"To set talent level: /talent <n or e or q> <value>"
);
return
;
}
if
(
nextLevel
>
16
){
CommandHandler
.
sendMessage
(
sender
,
"Invalid talent level. Level should be lower than 16"
);
return
;
}
// Upgrade skill
avatar
.
getSkillLevelMap
().
put
(
skillId
,
nextLevel
);
avatar
.
save
();
// Packet
sender
.
sendPacket
(
new
PacketAvatarSkillChangeNotify
(
avatar
,
skillId
,
currentLevel
,
nextLevel
));
sender
.
sendPacket
(
new
PacketAvatarSkillUpgradeRsp
(
avatar
,
skillId
,
currentLevel
,
nextLevel
));
CommandHandler
.
sendMessage
(
sender
,
"Set this talent to "
+
nextLevel
+
"."
);
}
catch
(
NumberFormatException
ignored
)
{
CommandHandler
.
sendMessage
(
sender
,
"Invalid talent level."
);
return
;
}
break
;
case
"getid"
:
EntityAvatar
entity
=
sender
.
getTeamManager
().
getCurrentAvatarEntity
();
...
...
src/main/java/emu/grasscutter/command/commands/TelePortCommand.java
0 → 100644
View file @
dce13cf6
package
emu.grasscutter.command.commands
;
import
emu.grasscutter.command.Command
;
import
emu.grasscutter.command.CommandHandler
;
import
emu.grasscutter.game.GenshinPlayer
;
import
emu.grasscutter.utils.Position
;
import
java.util.List
;
@Command
(
label
=
"teleport"
,
usage
=
"teleport <x> <y> <z>"
,
aliases
=
{
"tp"
},
description
=
"Change the player's position."
,
permission
=
"player.teleport"
)
public
class
TelePortCommand
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
()
<
3
){
CommandHandler
.
sendMessage
(
sender
,
"Usage: /tp <x> <y> <z> [scene id]"
);
return
;
}
try
{
float
x
=
0
f
;
float
y
=
0
f
;
float
z
=
0
f
;
if
(
args
.
get
(
0
).
contains
(
"~"
))
{
if
(
args
.
get
(
0
).
equals
(
"~"
))
{
x
=
sender
.
getPos
().
getX
();
}
else
{
x
=
Float
.
parseFloat
(
args
.
get
(
0
).
replace
(
"~"
,
""
))
+
sender
.
getPos
().
getX
();
}
}
else
{
x
=
Float
.
parseFloat
(
args
.
get
(
0
));
}
if
(
args
.
get
(
1
).
contains
(
"~"
))
{
if
(
args
.
get
(
1
).
equals
(
"~"
))
{
y
=
sender
.
getPos
().
getY
();
}
else
{
y
=
Float
.
parseFloat
(
args
.
get
(
1
).
replace
(
"~"
,
""
))
+
sender
.
getPos
().
getY
();
}
}
else
{
y
=
Float
.
parseFloat
(
args
.
get
(
1
));
}
if
(
args
.
get
(
2
).
contains
(
"~"
))
{
if
(
args
.
get
(
2
).
equals
(
"~"
))
{
z
=
sender
.
getPos
().
getZ
();
}
else
{
z
=
Float
.
parseFloat
(
args
.
get
(
2
).
replace
(
"~"
,
""
))
+
sender
.
getPos
().
getZ
();
}
}
else
{
z
=
Float
.
parseFloat
(
args
.
get
(
2
));
}
int
sceneId
=
sender
.
getSceneId
();
if
(
args
.
size
()
==
4
){
sceneId
=
Integer
.
parseInt
(
args
.
get
(
3
));
}
Position
target
=
new
Position
(
x
,
y
,
z
);
boolean
result
=
sender
.
getWorld
().
transferPlayerToScene
(
sender
,
sceneId
,
target
);
if
(!
result
)
{
CommandHandler
.
sendMessage
(
sender
,
"Invalid position."
);
}
}
catch
(
NumberFormatException
ignored
)
{
CommandHandler
.
sendMessage
(
sender
,
"Invalid position."
);
}
}
}
src/main/java/emu/grasscutter/data/GenshinData.java
View file @
dce13cf6
...
...
@@ -31,6 +31,7 @@ public class GenshinData {
private
static
final
Int2ObjectMap
<
AvatarSkillDepotData
>
avatarSkillDepotDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarSkillData
>
avatarSkillDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarCurveData
>
avatarCurveDataMap
=
new
Int2ObjectLinkedOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarFetterLevelData
>
avatarFetterLevelDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarPromoteData
>
avatarPromoteDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarTalentData
>
avatarTalentDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
ProudSkillData
>
proudSkillDataMap
=
new
Int2ObjectOpenHashMap
<>();
...
...
@@ -57,6 +58,8 @@ public class GenshinData {
private
static
final
Int2ObjectMap
<
SceneData
>
sceneDataMap
=
new
Int2ObjectLinkedOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
FetterData
>
fetterDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
FetterCharacterCardData
>
fetterCharacterCardDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
RewardData
>
rewardDataMap
=
new
Int2ObjectOpenHashMap
<>();
// Cache
private
static
Map
<
Integer
,
List
<
Integer
>>
fetters
=
new
HashMap
<>();
...
...
@@ -114,6 +117,14 @@ public class GenshinData {
return
playerLevelDataMap
;
}
public
static
Int2ObjectMap
<
AvatarFetterLevelData
>
getAvatarFetterLevelDataMap
()
{
return
avatarFetterLevelDataMap
;
}
public
static
Int2ObjectMap
<
FetterCharacterCardData
>
getFetterCharacterCardDataMap
()
{
return
fetterCharacterCardDataMap
;
}
public
static
Int2ObjectMap
<
AvatarLevelData
>
getAvatarLevelDataMap
()
{
return
avatarLevelDataMap
;
}
...
...
@@ -175,6 +186,11 @@ public class GenshinData {
AvatarLevelData
levelData
=
avatarLevelDataMap
.
get
(
level
);
return
levelData
!=
null
?
levelData
.
getExp
()
:
0
;
}
public
static
int
getAvatarFetterLevelExpRequired
(
int
level
)
{
AvatarFetterLevelData
levelData
=
avatarFetterLevelDataMap
.
get
(
level
);
return
levelData
!=
null
?
levelData
.
getExp
()
:
0
;
}
public
static
Int2ObjectMap
<
ProudSkillData
>
getProudSkillDataMap
()
{
return
proudSkillDataMap
;
...
...
@@ -228,6 +244,10 @@ public class GenshinData {
return
sceneDataMap
;
}
public
static
Int2ObjectMap
<
RewardData
>
getRewardDataMap
()
{
return
rewardDataMap
;
}
public
static
Map
<
Integer
,
List
<
Integer
>>
getFetterDataEntries
()
{
if
(
fetters
.
isEmpty
())
{
fetterDataMap
.
forEach
((
k
,
v
)
->
{
...
...
src/main/java/emu/grasscutter/data/common/OpenCondData.java
0 → 100644
View file @
dce13cf6
package
emu.grasscutter.data.common
;
import
java.util.List
;
public
class
OpenCondData
{
private
String
CondType
;
private
List
<
Integer
>
ParamList
;
public
String
getCondType
()
{
return
CondType
;
}
public
void
setCondType
(
String
condType
)
{
CondType
=
condType
;
}
public
List
<
Integer
>
getParamList
()
{
return
ParamList
;
}
public
void
setParamList
(
List
<
Integer
>
paramList
)
{
ParamList
=
paramList
;
}
}
src/main/java/emu/grasscutter/data/common/RewardItemData.java
0 → 100644
View file @
dce13cf6
package
emu.grasscutter.data.common
;
public
class
RewardItemData
{
private
int
ItemId
;
private
int
ItemCount
;
public
int
getItemId
()
{
return
ItemId
;
}
public
void
setItemId
(
int
itemId
)
{
ItemId
=
itemId
;
}
public
int
getItemCount
()
{
return
ItemCount
;
}
public
void
setItemCount
(
int
itemCount
)
{
ItemCount
=
itemCount
;
}
}
src/main/java/emu/grasscutter/data/def/AvatarData.java
View file @
dce13cf6
...
...
@@ -57,6 +57,8 @@ public class AvatarData extends GenshinResource {
private
IntList
abilities
;
private
List
<
Integer
>
fetters
;
private
int
nameCardRewardId
;
private
int
nameCardId
;
@Override
public
int
getId
(){
...
...
@@ -199,12 +201,28 @@ public class AvatarData extends GenshinResource {
return
fetters
;
}
public
int
getNameCardRewardId
()
{
return
nameCardRewardId
;
}
public
int
getNameCardId
()
{
return
nameCardId
;
}
@Override
public
void
onLoad
()
{
this
.
skillDepot
=
GenshinData
.
getAvatarSkillDepotDataMap
().
get
(
this
.
SkillDepotId
);
// Get fetters from GenshinData
this
.
fetters
=
GenshinData
.
getFetterDataEntries
().
get
(
this
.
Id
);
if
(
GenshinData
.
getFetterCharacterCardDataMap
().
get
(
this
.
Id
)
!=
null
)
{
this
.
nameCardRewardId
=
GenshinData
.
getFetterCharacterCardDataMap
().
get
(
this
.
Id
).
getRewardId
();
}
if
(
GenshinData
.
getRewardDataMap
().
get
(
this
.
nameCardRewardId
)
!=
null
)
{
this
.
nameCardId
=
GenshinData
.
getRewardDataMap
().
get
(
this
.
nameCardRewardId
).
getRewardItemList
().
get
(
0
).
getItemId
();
}
int
size
=
GenshinData
.
getAvatarCurveDataMap
().
size
();
this
.
hpGrowthCurve
=
new
float
[
size
];
...
...
src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java
0 → 100644
View file @
dce13cf6
package
emu.grasscutter.data.def
;
import
emu.grasscutter.data.GenshinResource
;
import
emu.grasscutter.data.ResourceType
;
@ResourceType
(
name
=
"AvatarFettersLevelExcelConfigData.json"
)
public
class
AvatarFetterLevelData
extends
GenshinResource
{
private
int
FetterLevel
;
private
int
NeedExp
;
@Override
public
int
getId
()
{
return
this
.
FetterLevel
;
}
public
int
getLevel
()
{
return
FetterLevel
;
}
public
int
getExp
()
{
return
NeedExp
;
}
}
src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java
0 → 100644
View file @
dce13cf6
package
emu.grasscutter.data.def
;
import
emu.grasscutter.data.GenshinResource
;
import
emu.grasscutter.data.ResourceType
;
import
emu.grasscutter.data.ResourceType.LoadPriority
;
@ResourceType
(
name
=
"FetterCharacterCardExcelConfigData.json"
,
loadPriority
=
LoadPriority
.
HIGHEST
)
public
class
FetterCharacterCardData
extends
GenshinResource
{
private
int
AvatarId
;
private
int
RewardId
;
@Override
public
int
getId
()
{
return
AvatarId
;
}
public
int
getRewardId
()
{
return
RewardId
;
}
@Override
public
void
onLoad
()
{
}
}
src/main/java/emu/grasscutter/data/def/FetterData.java
View file @
dce13cf6
package
emu.grasscutter.data.def
;
import
java.util.List
;
import
emu.grasscutter.data.GenshinResource
;
import
emu.grasscutter.data.ResourceType
;
import
emu.grasscutter.data.ResourceType.LoadPriority
;
import
emu.grasscutter.data.common.OpenCondData
;
@ResourceType
(
name
=
{
"FetterInfoExcelConfigData.json"
,
"FettersExcelConfigData.json"
,
"FetterStoryExcelConfigData.json"
},
loadPriority
=
LoadPriority
.
HIGHEST
)
public
class
FetterData
extends
GenshinResource
{
private
int
AvatarId
;
private
int
FetterId
;
private
List
<
OpenCondData
>
OpenCond
;
@Override
public
int
getId
()
{
...
...
@@ -18,6 +22,10 @@ public class FetterData extends GenshinResource {
return
AvatarId
;
}
public
List
<
OpenCondData
>
getOpenConds
()
{
return
OpenCond
;
}
@Override
public
void
onLoad
()
{
}
...
...
src/main/java/emu/grasscutter/data/def/RewardData.java
0 → 100644
View file @
dce13cf6
package
emu.grasscutter.data.def
;
import
java.util.List
;
import
emu.grasscutter.data.GenshinResource
;
import
emu.grasscutter.data.ResourceType
;
import
emu.grasscutter.data.common.RewardItemData
;
@ResourceType
(
name
=
"RewardExcelConfigData.json"
)
public
class
RewardData
extends
GenshinResource
{
public
int
RewardId
;
public
List
<
RewardItemData
>
RewardItemList
;
@Override
public
int
getId
()
{
return
RewardId
;
}
public
List
<
RewardItemData
>
getRewardItemList
()
{
return
RewardItemList
;
}
@Override
public
void
onLoad
()
{
}
}
src/main/java/emu/grasscutter/database/DatabaseManager.java
View file @
dce13cf6
...
...
@@ -101,7 +101,7 @@ public final class DatabaseManager {
}
public
static
synchronized
int
getNextId
(
Class
<?>
c
)
{
DatabaseCounter
counter
=
getDatastore
().
find
(
DatabaseCounter
.
class
).
filter
(
Filters
.
eq
(
"_id"
,
c
.
getName
())).
first
();
DatabaseCounter
counter
=
getDatastore
().
find
(
DatabaseCounter
.
class
).
filter
(
Filters
.
eq
(
"_id"
,
c
.
get
Simple
Name
())).
first
();
if
(
counter
==
null
)
{
counter
=
new
DatabaseCounter
(
c
.
getSimpleName
());
}
...
...
src/main/java/emu/grasscutter/game/GenshinPlayer.java
View file @
dce13cf6
package
emu.grasscutter.game
;
import
java.util.*
;
import
dev.morphia.annotations.*
;
import
emu.grasscutter.GenshinConstants
;
import
emu.grasscutter.Grasscutter
;
...
...
@@ -23,7 +21,6 @@ import emu.grasscutter.game.props.ActionReason;
import
emu.grasscutter.game.props.PlayerProperty
;
import
emu.grasscutter.net.packet.GenshinPacket
;
import
emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry
;
import
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday
;
import
emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry
;
import
emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage
;
import
emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType
;
...
...
@@ -35,38 +32,18 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import
emu.grasscutter.net.proto.WorldPlayerLocationInfoOuterClass.WorldPlayerLocationInfo
;
import
emu.grasscutter.server.game.GameServer
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.packet.send.PacketAbilityInvocationsNotify
;
import
emu.grasscutter.server.packet.send.PacketAvatarAddNotify
;
import
emu.grasscutter.server.packet.send.PacketAvatarDataNotify
;
import
emu.grasscutter.server.packet.send.PacketAvatarGainCostumeNotify
;
import
emu.grasscutter.server.packet.send.PacketAvatarGainFlycloakNotify
;
import
emu.grasscutter.server.packet.send.PacketClientAbilityInitFinishNotify
;
import
emu.grasscutter.server.packet.send.PacketCombatInvocationsNotify
;
import
emu.grasscutter.server.packet.send.PacketGadgetInteractRsp
;
import
emu.grasscutter.server.packet.send.PacketItemAddHintNotify
;
import
emu.grasscutter.server.packet.send.PacketOpenStateUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpResultNotify
;
import
emu.grasscutter.server.packet.send.PacketPlayerDataNotify
;
import
emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify
;
import
emu.grasscutter.server.packet.send.PacketPlayerPropNotify
;
import
emu.grasscutter.server.packet.send.PacketPlayerStoreNotify
;
import
emu.grasscutter.server.packet.send.PacketPrivateChatNotify
;
import
emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify
;
import
emu.grasscutter.server.packet.send.PacketSetNameCardRsp
;
import
emu.grasscutter.server.packet.send.PacketStoreWeightLimitNotify
;
import
emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify
;
import
emu.grasscutter.server.packet.send.PacketWorldPlayerLocationNotify
;
import
emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify
;
import
emu.grasscutter.server.packet.send.*
;
import
emu.grasscutter.utils.Position
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
import
java.util.*
;
@Entity
(
value
=
"players"
,
useDiscriminator
=
false
)
public
class
GenshinPlayer
{
@Id
private
int
id
;
@Indexed
(
options
=
@IndexOptions
(
unique
=
true
))
private
String
accountId
;
@Transient
private
Account
account
;
private
String
nickname
;
private
String
signature
;
...
...
@@ -75,12 +52,12 @@ public class GenshinPlayer {
private
Position
pos
;
private
Position
rotation
;
private
PlayerBirthday
birthday
;
private
Map
<
Integer
,
Integer
>
properties
;
private
Set
<
Integer
>
nameCardList
;
private
Set
<
Integer
>
flyCloakList
;
private
Set
<
Integer
>
costumeList
;
@Transient
private
long
nextGuid
=
0
;
@Transient
private
int
peerId
;
@Transient
private
World
world
;
...
...
@@ -89,32 +66,34 @@ public class GenshinPlayer {
@Transient
private
AvatarStorage
avatars
;
@Transient
private
Inventory
inventory
;
@Transient
private
FriendsList
friendsList
;
private
TeamManager
teamManager
;
private
PlayerGachaInfo
gachaInfo
;
private
PlayerProfile
playerProfile
;
private
MpSettingType
mpSetting
=
MpSettingType
.
MpSettingEnterAfterApply
;
private
boolean
showAvatar
;
private
ArrayList
<
AvatarProfileData
>
shownAvatars
;
private
Set
<
Integer
>
rewardedLevels
;
private
int
sceneId
;
private
int
regionId
;
private
int
mainCharacterId
;
private
boolean
godmode
;
@Transient
private
boolean
paused
;
@Transient
private
int
enterSceneToken
;
@Transient
private
SceneLoadState
sceneState
;
@Transient
private
boolean
hasSentAvatarDataNotify
;
@Transient
private
long
nextSendPlayerLocTime
=
0
;
@Transient
private
final
Int2ObjectMap
<
CoopRequest
>
coopRequests
;
@Transient
private
final
InvokeHandler
<
CombatInvokeEntry
>
combatInvokeHandler
;
@Transient
private
final
InvokeHandler
<
AbilityInvokeEntry
>
abilityInvokeHandler
;
@Transient
private
final
InvokeHandler
<
AbilityInvokeEntry
>
clientAbilityInitFinishHandler
;
@Deprecated
@SuppressWarnings
({
"rawtypes"
,
"unchecked"
})
// Morphia only!
public
GenshinPlayer
()
{
@Deprecated
@SuppressWarnings
({
"rawtypes"
,
"unchecked"
})
// Morphia only!
public
GenshinPlayer
()
{
this
.
inventory
=
new
Inventory
(
this
);
this
.
avatars
=
new
AvatarStorage
(
this
);
this
.
friendsList
=
new
FriendsList
(
this
);
...
...
@@ -127,24 +106,25 @@ public class GenshinPlayer {
}
this
.
properties
.
put
(
prop
.
getId
(),
0
);
}
this
.
gachaInfo
=
new
PlayerGachaInfo
();
this
.
nameCardList
=
new
HashSet
<>();
this
.
flyCloakList
=
new
HashSet
<>();
this
.
costumeList
=
new
HashSet
<>();
this
.
setSceneId
(
3
);
this
.
setRegionId
(
1
);
this
.
sceneState
=
SceneLoadState
.
NONE
;
this
.
coopRequests
=
new
Int2ObjectOpenHashMap
<>();
this
.
combatInvokeHandler
=
new
InvokeHandler
(
PacketCombatInvocationsNotify
.
class
);
this
.
abilityInvokeHandler
=
new
InvokeHandler
(
PacketAbilityInvocationsNotify
.
class
);
this
.
clientAbilityInitFinishHandler
=
new
InvokeHandler
(
PacketClientAbilityInitFinishNotify
.
class
);
this
.
birthday
=
new
PlayerBirthday
();
this
.
rewardedLevels
=
new
HashSet
<>();
}
// On player creation
public
GenshinPlayer
(
GameSession
session
)
{
this
();
...
...
@@ -176,7 +156,7 @@ public class GenshinPlayer {
public
void
setUid
(
int
id
)
{
this
.
id
=
id
;
}
public
long
getNextGenshinGuid
()
{
long
nextId
=
++
this
.
nextGuid
;
return
((
long
)
this
.
getUid
()
<<
32
)
+
nextId
;
...
...
@@ -198,23 +178,23 @@ public class GenshinPlayer {
public
void
setSession
(
GameSession
session
)
{
this
.
session
=
session
;
}
public
boolean
isOnline
()
{
return
this
.
getSession
()
!=
null
&&
this
.
getSession
().
isActive
();
}
public
GameServer
getServer
()
{
return
this
.
getSession
().
getServer
();
}
public
synchronized
World
getWorld
()
{
return
this
.
world
;
}
public
synchronized
void
setWorld
(
World
world
)
{
this
.
world
=
world
;
}
public
GenshinScene
getScene
()
{
return
scene
;
}
...
...
@@ -235,7 +215,7 @@ public class GenshinPlayer {
this
.
nickname
=
nickName
;
this
.
updateProfile
();
}
public
int
getHeadImage
()
{
return
headImage
;
}
...
...
@@ -257,71 +237,71 @@ public class GenshinPlayer {
public
Position
getPos
()
{
return
pos
;
}
public
Position
getRotation
()
{
return
rotation
;
}
public
int
getLevel
()
{
return
this
.
getProperty
(
PlayerProperty
.
PROP_PLAYER_LEVEL
);
}
public
int
getExp
()
{
return
this
.
getProperty
(
PlayerProperty
.
PROP_PLAYER_EXP
);
}
public
int
getWorldLevel
()
{
return
this
.
getProperty
(
PlayerProperty
.
PROP_PLAYER_WORLD_LEVEL
);
}
public
int
getPrimogems
()
{
return
this
.
getProperty
(
PlayerProperty
.
PROP_PLAYER_HCOIN
);
}
public
void
setPrimogems
(
int
primogem
)
{
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_HCOIN
,
primogem
);
this
.
sendPacket
(
new
PacketPlayerPropNotify
(
this
,
PlayerProperty
.
PROP_PLAYER_HCOIN
));
}
public
int
getMora
()
{
return
this
.
getProperty
(
PlayerProperty
.
PROP_PLAYER_SCOIN
);
}
public
void
setMora
(
int
mora
)
{
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_SCOIN
,
mora
);
this
.
sendPacket
(
new
PacketPlayerPropNotify
(
this
,
PlayerProperty
.
PROP_PLAYER_SCOIN
));
}
private
int
getExpRequired
(
int
level
)
{
PlayerLevelData
levelData
=
GenshinData
.
getPlayerLevelDataMap
().
get
(
level
);
return
levelData
!=
null
?
levelData
.
getExp
()
:
0
;
return
levelData
!=
null
?
levelData
.
getExp
()
:
0
;
}
private
float
getExpModifier
()
{
return
Grasscutter
.
getConfig
().
getGameServerOptions
().
getGameRates
().
ADVENTURE_EXP_RATE
;
}
// Affected by exp rate
public
void
earnExp
(
int
exp
)
{
addExpDirectly
((
int
)
(
exp
*
getExpModifier
()));
}
// Directly give player exp
public
void
addExpDirectly
(
int
gain
)
{
boolean
hasLeveledUp
=
false
;
int
level
=
getLevel
();
int
exp
=
getExp
();
int
reqExp
=
getExpRequired
(
level
);
exp
+=
gain
;
while
(
exp
>=
reqExp
&&
reqExp
>
0
)
{
exp
-=
reqExp
;
level
+=
1
;
reqExp
=
getExpRequired
(
level
);
hasLeveledUp
=
true
;
}
if
(
hasLeveledUp
)
{
// Set level property
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_LEVEL
,
level
);
...
...
@@ -330,18 +310,18 @@ public class GenshinPlayer {
// Update player with packet
this
.
sendPacket
(
new
PacketPlayerPropNotify
(
this
,
PlayerProperty
.
PROP_PLAYER_LEVEL
));
}
// Set exp
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_EXP
,
exp
);
// Update player with packet
this
.
sendPacket
(
new
PacketPlayerPropNotify
(
this
,
PlayerProperty
.
PROP_PLAYER_EXP
));
}
private
void
updateProfile
()
{
getProfile
().
syncWithCharacter
(
this
);
}
public
boolean
isFirstLoginEnterScene
()
{
return
!
this
.
hasSentAvatarDataNotify
;
}
...
...
@@ -364,11 +344,11 @@ public class GenshinPlayer {
public
Map
<
Integer
,
Integer
>
getProperties
()
{
return
properties
;
}
public
void
setProperty
(
PlayerProperty
prop
,
int
value
)
{
getProperties
().
put
(
prop
.
getId
(),
value
);
}
public
int
getProperty
(
PlayerProperty
prop
)
{
return
getProperties
().
get
(
prop
.
getId
());
}
...
...
@@ -376,11 +356,11 @@ public class GenshinPlayer {
public
Set
<
Integer
>
getFlyCloakList
()
{
return
flyCloakList
;
}
public
Set
<
Integer
>
getCostumeList
()
{
return
costumeList
;
}
public
Set
<
Integer
>
getNameCardList
()
{
return
this
.
nameCardList
;
}
...
...
@@ -388,7 +368,7 @@ public class GenshinPlayer {
public
MpSettingType
getMpSetting
()
{
return
mpSetting
;
}
public
synchronized
Int2ObjectMap
<
CoopRequest
>
getCoopRequests
()
{
return
coopRequests
;
}
...
...
@@ -396,7 +376,7 @@ public class GenshinPlayer {
public
InvokeHandler
<
CombatInvokeEntry
>
getCombatInvokeHandler
()
{
return
this
.
combatInvokeHandler
;
}
public
InvokeHandler
<
AbilityInvokeEntry
>
getAbilityInvokeHandler
()
{
return
this
.
abilityInvokeHandler
;
}
...
...
@@ -412,11 +392,11 @@ public class GenshinPlayer {
public
AvatarStorage
getAvatars
()
{
return
avatars
;
}
public
Inventory
getInventory
()
{
return
inventory
;
}
public
FriendsList
getFriendsList
()
{
return
this
.
friendsList
;
}
...
...
@@ -469,7 +449,7 @@ public class GenshinPlayer {
public
void
setPaused
(
boolean
newPauseState
)
{
boolean
oldPauseState
=
this
.
paused
;
this
.
paused
=
newPauseState
;
if
(
newPauseState
&&
!
oldPauseState
)
{
this
.
onPause
();
}
else
if
(
oldPauseState
&&
!
newPauseState
)
{
...
...
@@ -484,7 +464,7 @@ public class GenshinPlayer {
public
void
setSceneLoadState
(
SceneLoadState
sceneState
)
{
this
.
sceneState
=
sceneState
;
}
public
boolean
isInMultiplayer
()
{
return
this
.
getWorld
()
!=
null
&&
this
.
getWorld
().
isMultiplayer
();
}
...
...
@@ -504,7 +484,7 @@ public class GenshinPlayer {
public
void
setRegionId
(
int
regionId
)
{
this
.
regionId
=
regionId
;
}
public
boolean
inGodmode
()
{
return
godmode
;
}
...
...
@@ -523,11 +503,11 @@ public class GenshinPlayer {
public
void
addAvatar
(
GenshinAvatar
avatar
)
{
boolean
result
=
getAvatars
().
addAvatar
(
avatar
);
if
(
result
)
{
// Add starting weapon
getAvatars
().
addStartingWeapon
(
avatar
);
// Try adding to team if possible
//List<EntityAvatar> currentTeam = this.getTeamManager().getCurrentTeam();
boolean
addedToTeam
=
false
;
...
...
@@ -537,7 +517,7 @@ public class GenshinPlayer {
addedToTeam = currentTeam
}
*/
// Done
if
(
hasSentAvatarDataNotify
())
{
// Recalc stats
...
...
@@ -549,55 +529,56 @@ public class GenshinPlayer {
// Failed adding avatar
}
}
public
void
addFlycloak
(
int
flycloakId
)
{
this
.
getFlyCloakList
().
add
(
flycloakId
);
this
.
sendPacket
(
new
PacketAvatarGainFlycloakNotify
(
flycloakId
));
}
public
void
addCostume
(
int
costumeId
)
{
this
.
getCostumeList
().
add
(
costumeId
);
this
.
sendPacket
(
new
PacketAvatarGainCostumeNotify
(
costumeId
));
}
public
void
addNameCard
(
int
nameCardId
)
{
this
.
getNameCardList
().
add
(
nameCardId
);
this
.
sendPacket
(
new
PacketUnlockNameCardNotify
(
nameCardId
));
}
public
void
setNameCard
(
int
nameCardId
)
{
if
(!
this
.
getNameCardList
().
contains
(
nameCardId
))
{
return
;
}
this
.
setNameCardId
(
nameCardId
);
this
.
sendPacket
(
new
PacketSetNameCardRsp
(
nameCardId
));
}
public
void
dropMessage
(
Object
message
)
{
this
.
sendPacket
(
new
PacketPrivateChatNotify
(
GenshinConstants
.
SERVER_CONSOLE_UID
,
getUid
(),
message
.
toString
()));
}
/**
* Sends a message to another player.
* @param sender The sender of the message.
*
* @param sender The sender of the message.
* @param message The message to send.
*/
public
void
sendMessage
(
GenshinPlayer
sender
,
Object
message
)
{
this
.
sendPacket
(
new
PacketPrivateChatNotify
(
sender
.
getUid
(),
this
.
getUid
(),
message
.
toString
()));
}
public
void
interactWith
(
int
gadgetEntityId
)
{
GenshinEntity
entity
=
getScene
().
getEntityById
(
gadgetEntityId
);
if
(
entity
==
null
)
{
return
;
}
// Delete
entity
.
getScene
().
removeEntity
(
entity
);
// Handle
if
(
entity
instanceof
EntityItem
)
{
// Pick item
...
...
@@ -610,24 +591,24 @@ public class GenshinPlayer {
this
.
sendPacket
(
new
PacketItemAddHintNotify
(
item
,
ActionReason
.
SubfieldDrop
));
}
}
return
;
}
public
void
onPause
()
{
}
public
void
onUnpause
()
{
}
public
void
sendPacket
(
GenshinPacket
packet
)
{
if
(
this
.
hasSentAvatarDataNotify
)
{
this
.
getSession
().
send
(
packet
);
}
}
public
OnlinePlayerInfo
getOnlinePlayerInfo
()
{
OnlinePlayerInfo
.
Builder
onlineInfo
=
OnlinePlayerInfo
.
newBuilder
()
.
setUid
(
this
.
getUid
())
...
...
@@ -637,17 +618,17 @@ public class GenshinPlayer {
.
setNameCardId
(
this
.
getNameCardId
())
.
setSignature
(
this
.
getSignature
())
.
setAvatar
(
HeadImage
.
newBuilder
().
setAvatarId
(
this
.
getHeadImage
()));
if
(
this
.
getWorld
()
!=
null
)
{
onlineInfo
.
setCurPlayerNumInWorld
(
this
.
getWorld
().
getPlayers
().
indexOf
(
this
)
+
1
);
}
else
{
onlineInfo
.
setCurPlayerNumInWorld
(
1
);
}
return
onlineInfo
.
build
();
}
public
PlayerBirthday
getBirthday
(){
public
PlayerBirthday
getBirthday
()
{
return
this
.
birthday
;
}
...
...
@@ -656,6 +637,18 @@ public class GenshinPlayer {
this
.
updateProfile
();
}
public
boolean
hasBirthday
()
{
return
this
.
birthday
.
getDay
()
>
0
;
}
public
Set
<
Integer
>
getRewardedLevels
()
{
return
rewardedLevels
;
}
public
void
setRewardedLevels
(
Set
<
Integer
>
rewardedLevels
)
{
this
.
rewardedLevels
=
rewardedLevels
;
}
public
SocialDetail
.
Builder
getSocialDetail
()
{
SocialDetail
.
Builder
social
=
SocialDetail
.
newBuilder
()
.
setUid
(
this
.
getUid
())
...
...
@@ -671,22 +664,22 @@ public class GenshinPlayer {
.
setFinishAchievementNum
(
0
);
return
social
;
}
public
WorldPlayerLocationInfo
getWorldPlayerLocationInfo
()
{
return
WorldPlayerLocationInfo
.
newBuilder
()
.
setSceneId
(
this
.
getSceneId
())
.
setPlayerLoc
(
this
.
getPlayerLocationInfo
())
.
build
();
.
setSceneId
(
this
.
getSceneId
())
.
setPlayerLoc
(
this
.
getPlayerLocationInfo
())
.
build
();
}
public
PlayerLocationInfo
getPlayerLocationInfo
()
{
return
PlayerLocationInfo
.
newBuilder
()
.
setUid
(
this
.
getUid
())
.
setPos
(
this
.
getPos
().
toProto
())
.
setRot
(
this
.
getRotation
().
toProto
())
.
build
();
.
setUid
(
this
.
getUid
())
.
setPos
(
this
.
getPos
().
toProto
())
.
setRot
(
this
.
getRotation
().
toProto
())
.
build
();
}
public
synchronized
void
onTick
()
{
// Check ping
if
(
this
.
getLastPingTime
()
>
System
.
currentTimeMillis
()
+
60000
)
{
...
...
@@ -706,7 +699,7 @@ public class GenshinPlayer {
if
(
this
.
getWorld
()
!=
null
)
{
// RTT notify - very important to send this often
this
.
sendPacket
(
new
PacketWorldPlayerRTTNotify
(
this
.
getWorld
()));
// Update player locations if in multiplayer every 5 seconds
long
time
=
System
.
currentTimeMillis
();
if
(
this
.
getWorld
().
isMultiplayer
()
&&
this
.
getScene
()
!=
null
&&
time
>
nextSendPlayerLocTime
)
{
...
...
@@ -716,7 +709,7 @@ public class GenshinPlayer {
}
}
}
public
void
resetSendPlayerLocTime
()
{
this
.
nextSendPlayerLocTime
=
System
.
currentTimeMillis
()
+
5000
;
}
...
...
@@ -725,7 +718,7 @@ public class GenshinPlayer {
private
void
onLoad
()
{
this
.
getTeamManager
().
setPlayer
(
this
);
}
public
void
save
()
{
DatabaseHelper
.
savePlayer
(
this
);
}
...
...
@@ -734,78 +727,80 @@ public class GenshinPlayer {
// Make sure these exist
if
(
this
.
getTeamManager
()
==
null
)
{
this
.
teamManager
=
new
TeamManager
(
this
);
}
if
(
this
.
getProfile
().
getUid
()
==
0
)
{
}
if
(
this
.
getProfile
().
getUid
()
==
0
)
{
this
.
getProfile
().
syncWithCharacter
(
this
);
}
// Check if player object exists in server
// TODO - optimize
GenshinPlayer
exists
=
this
.
getServer
().
getPlayerByUid
(
getUid
());
if
(
exists
!=
null
)
{
exists
.
getSession
().
close
();
}
// Load from db
this
.
getAvatars
().
loadFromDatabase
();
this
.
getInventory
().
loadFromDatabase
();
this
.
getAvatars
().
postLoad
();
this
.
getFriendsList
().
loadFromDatabase
();
// Create world
World
world
=
new
World
(
this
);
world
.
addPlayer
(
this
);
// Add to gameserver
if
(
getSession
().
isActive
())
{
getServer
().
registerPlayer
(
this
);
getProfile
().
setPlayer
(
this
);
// Set online
}
// Multiplayer setting
// Multiplayer setting
this
.
setProperty
(
PlayerProperty
.
PROP_PLAYER_MP_SETTING_TYPE
,
this
.
getMpSetting
().
getNumber
());
this
.
setProperty
(
PlayerProperty
.
PROP_IS_MP_MODE_AVAILABLE
,
1
);
// Packets
session
.
send
(
new
PacketPlayerDataNotify
(
this
));
// Player data
session
.
send
(
new
PacketStoreWeightLimitNotify
());
session
.
send
(
new
PacketPlayerStoreNotify
(
this
));
session
.
send
(
new
PacketAvatarDataNotify
(
this
));
session
.
send
(
new
PacketPlayerEnterSceneNotify
(
this
));
// Enter game world
session
.
send
(
new
PacketPlayerLevelRewardUpdateNotify
(
rewardedLevels
));
session
.
send
(
new
PacketOpenStateUpdateNotify
());
// First notify packets sent
this
.
setHasSentAvatarDataNotify
(
true
);
}
public
void
onLogout
()
{
// Leave world
if
(
this
.
getWorld
()
!=
null
)
{
this
.
getWorld
().
removePlayer
(
this
);
}
// Status stuff
this
.
getProfile
().
syncWithCharacter
(
this
);
this
.
getProfile
().
setPlayer
(
null
);
// Set offline
this
.
getCoopRequests
().
clear
();
// Save to db
this
.
save
();
this
.
getTeamManager
().
saveAvatars
();
this
.
getFriendsList
().
save
();
}
public
enum
SceneLoadState
{
NONE
(
0
),
LOADING
(
1
),
INIT
(
2
),
LOADED
(
3
);
NONE
(
0
),
LOADING
(
1
),
INIT
(
2
),
LOADED
(
3
);
private
final
int
value
;
private
SceneLoadState
(
int
value
)
{
this
.
value
=
value
;
}
public
int
getValue
()
{
return
this
.
value
;
}
...
...
src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java
View file @
dce13cf6
...
...
@@ -89,6 +89,12 @@ public class GenshinAvatar {
private
int
flyCloak
;
private
int
costume
;
private
int
bornTime
;
private
int
fetterLevel
=
1
;
private
int
fetterExp
;
private
int
nameCardRewardId
;
private
int
nameCardId
;
public
GenshinAvatar
()
{
// Morhpia only!
...
...
@@ -107,6 +113,8 @@ public class GenshinAvatar {
public
GenshinAvatar
(
AvatarData
data
)
{
this
();
this
.
avatarId
=
data
.
getId
();
this
.
nameCardRewardId
=
data
.
getNameCardRewardId
();
this
.
nameCardId
=
data
.
getNameCardId
();
this
.
data
=
data
;
this
.
bornTime
=
(
int
)
(
System
.
currentTimeMillis
()
/
1000
);
this
.
flyCloak
=
140001
;
...
...
@@ -169,6 +177,14 @@ public class GenshinAvatar {
this
.
satiation
=
satiation
;
}
public
int
getNameCardRewardId
()
{
return
nameCardRewardId
;
}
public
void
setNameCardRewardId
(
int
nameCardRewardId
)
{
this
.
nameCardRewardId
=
nameCardRewardId
;
}
public
int
getSatiationPenalty
()
{
return
satiationPenalty
;
}
...
...
@@ -281,6 +297,30 @@ public class GenshinAvatar {
return
fetters
;
}
public
int
getFetterLevel
()
{
return
fetterLevel
;
}
public
void
setFetterLevel
(
int
fetterLevel
)
{
this
.
fetterLevel
=
fetterLevel
;
}
public
int
getFetterExp
()
{
return
fetterExp
;
}
public
void
setFetterExp
(
int
fetterExp
)
{
this
.
fetterExp
=
fetterExp
;
}
public
int
getNameCardId
()
{
return
nameCardId
;
}
public
void
setNameCardId
(
int
nameCardId
)
{
this
.
nameCardId
=
nameCardId
;
}
public
float
getCurrentHp
()
{
return
currentHp
;
}
...
...
@@ -403,6 +443,8 @@ public class GenshinAvatar {
// Fetters
this
.
setFetterList
(
data
.
getFetters
());
this
.
setNameCardRewardId
(
data
.
getNameCardRewardId
());
this
.
setNameCardId
(
data
.
getNameCardId
());
// Get hp percent, set to 100% if none
float
hpPercent
=
this
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
)
<=
0
?
1
f
:
this
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
)
/
this
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
);
...
...
@@ -701,9 +743,14 @@ public class GenshinAvatar {
}
public
AvatarInfo
toProto
()
{
int
fetterLevel
=
this
.
getFetterLevel
();
AvatarFetterInfo
.
Builder
avatarFetter
=
AvatarFetterInfo
.
newBuilder
()
.
setExpLevel
(
10
)
.
setExpNumber
(
6325
);
// Highest Level
.
setExpLevel
(
fetterLevel
);
if
(
fetterLevel
!=
10
)
{
avatarFetter
.
setExpNumber
(
this
.
getFetterExp
());
}
if
(
this
.
getFetterList
()
!=
null
)
{
for
(
int
i
=
0
;
i
<
this
.
getFetterList
().
size
();
i
++)
{
...
...
@@ -715,6 +762,12 @@ public class GenshinAvatar {
}
}
int
cardId
=
this
.
getNameCardId
();
if
(
this
.
getPlayer
().
getNameCardList
().
contains
(
cardId
))
{
avatarFetter
.
addRewardedFetterLevelList
(
10
);
}
AvatarInfo
.
Builder
avatarInfo
=
AvatarInfo
.
newBuilder
()
.
setAvatarId
(
this
.
getAvatarId
())
.
setGuid
(
this
.
getGuid
())
...
...
src/main/java/emu/grasscutter/game/inventory/GenshinItem.java
View file @
dce13cf6
...
...
@@ -90,7 +90,7 @@ public class GenshinItem {
// Equip data
if
(
getItemType
()
==
ItemType
.
ITEM_WEAPON
)
{
this
.
level
=
1
;
this
.
level
=
this
.
count
>
1
?
this
.
count
:
1
;
this
.
affixes
=
new
ArrayList
<>(
2
);
if
(
getItemData
().
getSkillAffix
()
!=
null
)
{
for
(
int
skillAffix
:
getItemData
().
getSkillAffix
())
{
...
...
src/main/java/emu/grasscutter/game/managers/InventoryManager.java
View file @
dce13cf6
...
...
@@ -711,6 +711,31 @@ public class InventoryManager {
player
.
sendPacket
(
new
PacketAvatarUpgradeRsp
(
avatar
,
oldLevel
,
oldPropMap
));
}
public
void
upgradeAvatarFetterLevel
(
GenshinPlayer
player
,
GenshinAvatar
avatar
,
int
expGain
)
{
// May work. Not test.
int
maxLevel
=
10
;
// Keep it until I think of a more "elegant" way
int
level
=
avatar
.
getFetterLevel
();
int
exp
=
avatar
.
getFetterExp
();
int
reqExp
=
GenshinData
.
getAvatarFetterLevelExpRequired
(
level
);
while
(
expGain
>
0
&&
reqExp
>
0
&&
level
<
maxLevel
)
{
int
toGain
=
Math
.
min
(
expGain
,
reqExp
-
exp
);
exp
+=
toGain
;
expGain
-=
toGain
;
if
(
exp
>=
reqExp
)
{
exp
=
0
;
level
+=
1
;
reqExp
=
GenshinData
.
getAvatarFetterLevelExpRequired
(
level
);
}
}
avatar
.
setFetterLevel
(
level
);
avatar
.
setFetterExp
(
exp
);
avatar
.
save
();
player
.
sendPacket
(
new
PacketAvatarPropNotify
(
avatar
));
}
public
void
upgradeAvatarSkill
(
GenshinPlayer
player
,
long
guid
,
int
skillId
)
{
// Sanity checks
GenshinAvatar
avatar
=
player
.
getAvatars
().
getAvatarByGuid
(
guid
);
...
...
src/main/java/emu/grasscutter/game/player/PlayerBirthday.java
View file @
dce13cf6
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
;
...
...
src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java
View file @
dce13cf6
...
...
@@ -64,9 +64,8 @@ public class MihoyoKcpServer extends Thread {
// Wait until the server socket is closed.
f
.
channel
().
closeFuture
().
sync
();
}
catch
(
Exception
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
Exception
exception
)
{
Grasscutter
.
getLogger
().
error
(
"Unable to start game server."
,
exception
);
}
finally
{
// Close
finish
();
...
...
src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java
View file @
dce13cf6
...
...
@@ -26,6 +26,7 @@ import emu.grasscutter.utils.Utils;
import
javax.net.ssl.KeyManagerFactory
;
import
javax.net.ssl.SSLContext
;
import
java.io.*
;
import
java.net.BindException
;
import
java.net.InetSocketAddress
;
import
java.net.URI
;
import
java.net.URLDecoder
;
...
...
@@ -39,47 +40,48 @@ public final class DispatchServer {
private
final
InetSocketAddress
address
;
private
final
Gson
gson
;
private
final
String
defaultServerName
=
"os_usa"
;
public
String
regionListBase64
;
public
HashMap
<
String
,
RegionData
>
regions
;
public
DispatchServer
()
{
this
.
regions
=
new
HashMap
<
String
,
RegionData
>();
this
.
address
=
new
InetSocketAddress
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
Ip
,
Grasscutter
.
getConfig
().
getDispatchOptions
().
Port
);
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
()
{
// Needs to be fixed by having the game servers connect to the dispatch server.
if
(
Grasscutter
.
getConfig
().
RunMode
.
equalsIgnoreCase
(
"HYBRID"
))
{
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
));
...
...
@@ -92,40 +94,58 @@ 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
);
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
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
(
"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
)
.
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
();
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
().
PublicPort
!=
0
?
Grasscutter
.
getConfig
().
getGameServerOptions
().
PublicPort
:
Grasscutter
.
getConfig
().
getGameServerOptions
().
Port
)
.
setSecretKey
(
ByteString
.
copyFrom
(
FileUtils
.
read
(
Grasscutter
.
getConfig
().
KEY_FOLDER
+
"dispatchSeed.bin"
)))
.
setIp
((
Grasscutter
.
getConfig
().
getGameServerOptions
().
PublicIp
.
isEmpty
()
?
Grasscutter
.
getConfig
().
getGameServerOptions
().
Ip
:
Grasscutter
.
getConfig
().
getGameServerOptions
().
PublicIp
))
.
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"
)))
.
build
();
QueryCurrRegionHttpRsp
parsedRegionQuery
=
regionQuery
.
toBuilder
().
setRegionInfo
(
serverRegion
).
build
();
regions
.
put
(
defaultServerName
,
new
RegionData
(
parsedRegionQuery
,
Base64
.
getEncoder
().
encodeToString
(
parsedRegionQuery
.
toByteString
().
toByteArray
())));
regions
.
put
(
defaultServerName
,
new
RegionData
(
parsedRegionQuery
,
Base64
.
getEncoder
().
encodeToString
(
parsedRegionQuery
.
toByteString
().
toByteArray
())));
}
else
{
if
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
getGameServers
().
length
==
0
)
{
Grasscutter
.
getLogger
().
error
(
"[Dispatch] There are no game servers available. Exiting due to unplayable state."
);
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
);
}
}
for
(
Config
.
DispatchServerOptions
.
RegionInfo
regionInfo
:
Grasscutter
.
getConfig
().
getDispatchOptions
().
getGameServers
())
{
if
(
usedNames
.
contains
(
regionInfo
.
Name
))
{
for
(
Config
.
DispatchServerOptions
.
RegionInfo
regionInfo
:
Grasscutter
.
getConfig
().
getDispatchOptions
()
.
getGameServers
())
{
if
(
usedNames
.
contains
(
regionInfo
.
Name
))
{
Grasscutter
.
getLogger
().
error
(
"Region name already in use."
);
continue
;
}
...
...
@@ -133,7 +153,12 @@ public final class DispatchServer {
.
setName
(
regionInfo
.
Name
)
.
setTitle
(
regionInfo
.
Title
)
.
setType
(
"DEV_PUBLIC"
)
.
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
)
.
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
();
usedNames
.
add
(
regionInfo
.
Name
);
servers
.
add
(
server
);
...
...
@@ -141,19 +166,21 @@ public final class DispatchServer {
RegionInfo
serverRegion
=
regionQuery
.
getRegionInfo
().
toBuilder
()
.
setIp
(
regionInfo
.
Ip
)
.
setPort
(
regionInfo
.
Port
)
.
setSecretKey
(
ByteString
.
copyFrom
(
FileUtils
.
read
(
Grasscutter
.
getConfig
().
KEY_FOLDER
+
"dispatchSeed.bin"
)))
.
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
())));
regions
.
put
(
regionInfo
.
Name
,
new
RegionData
(
parsedRegionQuery
,
Base64
.
getEncoder
().
encodeToString
(
parsedRegionQuery
.
toByteString
().
toByteArray
())));
}
QueryRegionListHttpRsp
regionList
=
QueryRegionListHttpRsp
.
newBuilder
()
.
addAllServers
(
servers
)
.
setClientSecretKey
(
rl
.
getClientSecretKey
())
.
setClientCustomConfigEncrypted
(
rl
.
getClientCustomConfigEncrypted
())
.
setEnableLoginPc
(
true
)
.
build
();
.
addAllServers
(
servers
)
.
setClientSecretKey
(
rl
.
getClientSecretKey
())
.
setClientCustomConfigEncrypted
(
rl
.
getClientCustomConfigEncrypted
())
.
setEnableLoginPc
(
true
)
.
build
();
this
.
regionListBase64
=
Base64
.
getEncoder
().
encodeToString
(
regionList
.
toByteString
().
toByteArray
());
}
catch
(
Exception
e
)
{
...
...
@@ -161,52 +188,92 @@ 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
{
HttpServer
server
;
if
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
UseSSL
)
{
HttpsServer
httpsServer
;
httpsServer
=
HttpsServer
.
create
(
getAddress
(),
0
);
HttpsServer
httpsServer
=
HttpsServer
.
create
(
getAddress
(),
0
);
SSLContext
sslContext
=
SSLContext
.
getInstance
(
"TLS"
);
try
(
FileInputStream
fis
=
new
FileInputStream
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
KeystorePath
))
{
char
[]
keystorePassword
=
Grasscutter
.
getConfig
().
getDispatchOptions
().
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
);
KeyManagerFactory
_kmf
;
try
{
KeyStore
ks
=
KeyStore
.
getInstance
(
"PKCS12"
);
ks
.
load
(
fis
,
keystorePassword
);
KeyManagerFactory
kmf
=
KeyManagerFactory
.
getInstance
(
"SunX509"
);
_kmf
=
kmf
;
kmf
.
init
(
ks
,
keystorePassword
);
}
catch
(
Exception
originalEx
)
{
try
{
// try to initialize kmf with the default password
char
[]
defaultPassword
=
"123456"
.
toCharArray
();
Grasscutter
.
getLogger
()
.
warn
(
"[Dispatch] Unable to load keystore. Trying default keystore password..."
);
KeyStore
ks
=
KeyStore
.
getInstance
(
"PKCS12"
);
ks
.
load
(
fis
,
defaultPassword
);
KeyManagerFactory
kmf
=
KeyManagerFactory
.
getInstance
(
"SunX509"
);
kmf
.
init
(
ks
,
defaultPassword
);
_kmf
=
kmf
;
Grasscutter
.
getLogger
().
warn
(
"[Dispatch] The default keystore password was loaded successfully. Please consider setting the password in config.json."
);
}
catch
(
Exception
ignored
)
{
Grasscutter
.
getLogger
().
warn
(
"[Dispatch] Error while loading keystore!"
);
// don't care about the exception for the "123456" default password attempt
originalEx
.
printStackTrace
();
throw
originalEx
;
}
}
sslContext
.
init
(
_kmf
.
getKeyManagers
(),
null
,
null
);
httpsServer
.
setHttpsConfigurator
(
new
HttpsConfigurator
(
sslContext
));
server
=
httpsServer
;
}
catch
(
BindException
ignored
)
{
Grasscutter
.
getLogger
().
error
(
"Unable to bind to port: "
+
getAddress
().
getPort
()
+
" (HTTPS)"
);
server
=
this
.
safelyCreateServer
(
this
.
getAddress
());
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
warn
(
"[Dispatch] No SSL cert found! Falling back to HTTP server."
);
Grasscutter
.
getConfig
().
getDispatchOptions
().
UseSSL
=
false
;
server
=
HttpServer
.
create
(
getAddress
()
,
0
);
server
=
this
.
safelyCreateServer
(
this
.
getAddress
());
}
}
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"
));
// Dispatch
server
.
createContext
(
"/query_region_list"
,
t
->
{
// Log incoming request.
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s request: query_region_list"
,
t
.
getRemoteAddress
()));
// Invoke event.
QueryAllRegionsEvent
event
=
new
QueryAllRegionsEvent
(
this
.
regionListBase64
);
event
.
call
();
// Respond with event result.
responseHTML
(
t
,
event
.
getRegionList
());
// Log
Grasscutter
.
getLogger
()
.
info
(
String
.
format
(
"[Dispatch] Client %s request: query_region_list"
,
t
.
getRemoteAddress
()));
responseHTML
(
t
,
regionListBase64
);
});
for
(
String
regionName
:
regions
.
keySet
())
{
server
.
createContext
(
"/query_cur_region_"
+
regionName
,
t
->
{
String
regionCurrentBase64
=
regions
.
get
(
regionName
).
Base64
;
// Log
incoming request.
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"Client %s request: query_cur_region_%s"
,
t
.
getRemoteAddress
(),
regionName
));
// Create a response f
r
om the request query parameters
.
// Log
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"Client %s request: query_cur_region_%s"
,
t
.
getRemoteAddress
(),
regionName
));
// Create a response fo
r
m the request query parameters
URI
uri
=
t
.
getRequestURI
();
String
response
=
"CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="
;
if
(
uri
.
getQuery
()
!=
null
&&
uri
.
getQuery
().
length
()
>
0
)
{
...
...
@@ -227,7 +294,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
)
{
...
...
@@ -235,16 +303,19 @@ public final class DispatchServer {
}
LoginResultJson
responseData
=
new
LoginResultJson
();
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s is trying to log in"
,
t
.
getRemoteAddress
()));
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
// Account doesnt exist, so we can either auto create it if the config value is
// set
if
(
Grasscutter
.
getConfig
().
getDispatchOptions
().
AutomaticallyCreateAccounts
)
{
// This account has been created AUTOMATICALLY. There will be no permissions added.
// This account has been created AUTOMATICALLY. There will be no permissions
// added.
account
=
DatabaseHelper
.
createAccountWithId
(
requestData
.
account
,
0
);
if
(
account
!=
null
)
{
...
...
@@ -253,19 +324,23 @@ public final class DispatchServer {
responseData
.
data
.
account
.
token
=
account
.
generateSessionKey
();
responseData
.
data
.
account
.
email
=
account
.
getEmail
();
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s failed to log in: Account %s created"
,
t
.
getRemoteAddress
(),
responseData
.
data
.
account
.
uid
));
Grasscutter
.
getLogger
()
.
info
(
String
.
format
(
"[Dispatch] Client %s failed to log in: Account %s created"
,
t
.
getRemoteAddress
(),
responseData
.
data
.
account
.
uid
));
}
else
{
responseData
.
retcode
=
-
201
;
responseData
.
message
=
"Username not found, create failed."
;
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s failed to log in: Account create failed"
,
t
.
getRemoteAddress
()));
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s failed to log in: Account create failed"
,
t
.
getRemoteAddress
()));
}
}
else
{
responseData
.
retcode
=
-
201
;
responseData
.
message
=
"Username not found."
;
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s failed to log in: Account no found"
,
t
.
getRemoteAddress
()));
}
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"
;
...
...
@@ -273,7 +348,8 @@ public final class DispatchServer {
responseData
.
data
.
account
.
token
=
account
.
generateSessionKey
();
responseData
.
data
.
account
.
email
=
account
.
getEmail
();
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s logged in as %s"
,
t
.
getRemoteAddress
(),
responseData
.
data
.
account
.
uid
));
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s logged in as %s"
,
t
.
getRemoteAddress
(),
responseData
.
data
.
account
.
uid
));
}
responseJSON
(
t
,
responseData
);
...
...
@@ -285,31 +361,35 @@ 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
;
}
LoginResultJson
responseData
=
new
LoginResultJson
();
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s is trying to log in via token"
,
t
.
getRemoteAddress
()));
Grasscutter
.
getLogger
()
.
info
(
String
.
format
(
"[Dispatch] Client %s is trying to log in via token"
,
t
.
getRemoteAddress
()));
// Login
Account
account
=
DatabaseHelper
.
getAccountById
(
requestData
.
uid
);
// Test
if
(
account
==
null
||
!
account
.
getSessionKey
().
equals
(
requestData
.
token
))
{
responseData
.
retcode
=
-
111
;
responseData
.
message
=
"Game account cache information error"
;
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s failed to log in via token"
,
t
.
getRemoteAddress
()));
Grasscutter
.
getLogger
()
.
info
(
String
.
format
(
"[Dispatch] Client %s failed to log in via token"
,
t
.
getRemoteAddress
()));
}
else
{
responseData
.
message
=
"OK"
;
responseData
.
data
.
account
.
uid
=
requestData
.
uid
;
responseData
.
data
.
account
.
token
=
requestData
.
token
;
responseData
.
data
.
account
.
email
=
account
.
getEmail
();
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s logged in via token as %s"
,
t
.
getRemoteAddress
(),
responseData
.
data
.
account
.
uid
));
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s logged in via token as %s"
,
t
.
getRemoteAddress
(),
responseData
.
data
.
account
.
uid
));
}
responseJSON
(
t
,
responseData
);
...
...
@@ -321,113 +401,112 @@ 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
;
}
LoginTokenData
loginData
=
getGsonFactory
().
fromJson
(
requestData
.
data
,
LoginTokenData
.
class
);
// Get login data
LoginTokenData
loginData
=
getGsonFactory
().
fromJson
(
requestData
.
data
,
LoginTokenData
.
class
);
// Get login
// data
ComboTokenResJson
responseData
=
new
ComboTokenResJson
();
// Login
Account
account
=
DatabaseHelper
.
getAccountById
(
loginData
.
uid
);
// Test
if
(
account
==
null
||
!
account
.
getSessionKey
().
equals
(
loginData
.
token
))
{
responseData
.
retcode
=
-
201
;
responseData
.
message
=
"Wrong session key."
;
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s failed to exchange combo token"
,
t
.
getRemoteAddress
()));
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s failed to exchange combo token"
,
t
.
getRemoteAddress
()));
}
else
{
responseData
.
message
=
"OK"
;
responseData
.
data
.
open_id
=
loginData
.
uid
;
responseData
.
data
.
combo_id
=
"157795300"
;
responseData
.
data
.
combo_token
=
account
.
generateLoginToken
();
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s succeed to exchange combo token"
,
t
.
getRemoteAddress
()));
Grasscutter
.
getLogger
().
info
(
String
.
format
(
"[Dispatch] Client %s succeed to exchange combo token"
,
t
.
getRemoteAddress
()));
}
responseJSON
(
t
,
responseData
);
});
// Agreement and Protocol
server
.
createContext
(
// hk4e-sdk-os.hoyoverse.com
"/hk4e_global/mdk/agreement/api/getAgreementInfos"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}"
)
);
"/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"
,
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\":\"\"}}}"
)
);
"/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"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}"
)
);
"/common/hk4e_global/announcement/api/getAlertPic"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}"
));
server
.
createContext
(
// hk4e-api-os.hoyoverse.com
"/common/hk4e_global/announcement/api/getAlertAnn"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}"
)
);
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"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0,\"type_list\":[],\"alert\":false,\"alert_id\":0,\"timezone\":0,\"t\":\""
+
System
.
currentTimeMillis
()
+
"\"}}"
)
);
"/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"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0}}"
)
);
"/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"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}"
)
);
"/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"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"c8820f246a5241ab9973f71df3ddd791\",\"action\":\"\",\"geetest\":{\"challenge\":\"\",\"gt\":\"\",\"new_captcha\":0,\"success\":1}}}"
)
);
// Config
"/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"
,
new
DispatchHttpJsonHandler
(
"{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}"
)
);
"/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"
,
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}}"
)
);
"/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"
,
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}}}}"
)
);
"/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"
,
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
"/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"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
"/log/sdk/upload"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
));
server
.
createContext
(
// log-upload-os.mihoyo.com
"/sdk/upload"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
"/sdk/upload"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
));
server
.
createContext
(
// /perf/config/verify?device_id=xxx&platform=x&name=xxx
"/perf/config/verify"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
"/perf/config/verify"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
));
// Logging servers
server
.
createContext
(
// overseauspider.yuanshen.com
"/log"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
));
server
.
createContext
(
// log-upload-os.mihoyo.com
"/crash/dataUpload"
,
new
DispatchHttpJsonHandler
(
"{\"code\":0}"
)
);
server
.
createContext
(
"/gacha"
,
t
->
responseHTML
(
t
,
"<!doctype html><html lang=\"en\"><head><title>Gacha</title></head><body></body></html>"
));
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
();
...
...
@@ -450,12 +529,12 @@ public final class DispatchServer {
// Set the response header status and length
t
.
getResponseHeaders
().
put
(
"Content-Type"
,
Collections
.
singletonList
(
"text/html; charset=UTF-8"
));
t
.
sendResponseHeaders
(
200
,
response
.
getBytes
().
length
);
//Write the response string
//
Write the response string
OutputStream
os
=
t
.
getResponseBody
();
os
.
write
(
response
.
getBytes
());
os
.
close
();
}
private
Map
<
String
,
String
>
parseQueryString
(
String
qs
)
{
Map
<
String
,
String
>
result
=
new
HashMap
<>();
if
(
qs
==
null
)
{
...
...
@@ -475,7 +554,8 @@ public final class DispatchServer {
if
(
eqPos
<
0
||
eqPos
>
next
)
{
result
.
put
(
URLDecoder
.
decode
(
qs
.
substring
(
last
,
next
),
"utf-8"
),
""
);
}
else
{
result
.
put
(
URLDecoder
.
decode
(
qs
.
substring
(
last
,
eqPos
),
"utf-8"
),
URLDecoder
.
decode
(
qs
.
substring
(
eqPos
+
1
,
next
),
"utf-8"
));
result
.
put
(
URLDecoder
.
decode
(
qs
.
substring
(
last
,
eqPos
),
"utf-8"
),
URLDecoder
.
decode
(
qs
.
substring
(
eqPos
+
1
,
next
),
"utf-8"
));
}
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
RuntimeException
(
e
);
// will never happen, utf-8 support is mandatory for java
...
...
src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java
0 → 100644
View file @
dce13cf6
package
emu.grasscutter.server.packet.recv
;
import
emu.grasscutter.data.GenshinData
;
import
emu.grasscutter.data.def.RewardData
;
import
emu.grasscutter.game.avatar.GenshinAvatar
;
import
emu.grasscutter.game.inventory.GenshinItem
;
import
emu.grasscutter.game.props.ActionReason
;
import
emu.grasscutter.net.packet.Opcodes
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.AvatarFetterLevelRewardReqOuterClass.AvatarFetterLevelRewardReq
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.packet.send.PacketAvatarDataNotify
;
import
emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify
;
import
emu.grasscutter.server.packet.send.PacketAvatarFetterLevelRewardRsp
;
import
emu.grasscutter.server.packet.send.PacketItemAddHintNotify
;
import
emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify
;
import
emu.grasscutter.net.packet.PacketHandler
;
@Opcodes
(
PacketOpcodes
.
AvatarFetterLevelRewardReq
)
public
class
HandlerAvatarFetterLevelRewardReq
extends
PacketHandler
{
@Override
public
void
handle
(
GameSession
session
,
byte
[]
header
,
byte
[]
payload
)
throws
Exception
{
AvatarFetterLevelRewardReq
req
=
AvatarFetterLevelRewardReq
.
parseFrom
(
payload
);
if
(
req
.
getFetterLevel
()
<
10
)
{
// You don't have a full level of fetter level, why do you want to get a divorce certificate?
session
.
send
(
new
PacketAvatarFetterLevelRewardRsp
(
req
.
getAvatarGuid
(),
req
.
getFetterLevel
()));
}
else
{
long
avatarGuid
=
req
.
getAvatarGuid
();
GenshinAvatar
avatar
=
session
.
getPlayer
()
.
getAvatars
()
.
getAvatarByGuid
(
avatarGuid
);
int
rewardId
=
avatar
.
getNameCardRewardId
();
RewardData
card
=
GenshinData
.
getRewardDataMap
().
get
(
rewardId
);
int
cardId
=
card
.
getRewardItemList
().
get
(
0
).
getItemId
();
if
(
session
.
getPlayer
().
getNameCardList
().
contains
(
cardId
))
{
// Already got divorce certificate.
session
.
getPlayer
().
sendPacket
(
new
PacketAvatarFetterLevelRewardRsp
(
req
.
getAvatarGuid
(),
req
.
getFetterLevel
(),
rewardId
));
return
;
}
GenshinItem
item
=
new
GenshinItem
(
cardId
);
session
.
getPlayer
().
getInventory
().
addItem
(
item
);
session
.
getPlayer
().
sendPacket
(
new
PacketItemAddHintNotify
(
item
,
ActionReason
.
FetterLevelReward
));
session
.
getPlayer
().
sendPacket
(
new
PacketUnlockNameCardNotify
(
cardId
));
session
.
send
(
new
PacketAvatarFetterDataNotify
(
avatar
));
session
.
send
(
new
PacketAvatarDataNotify
(
avatar
.
getPlayer
()));
session
.
send
(
new
PacketAvatarFetterLevelRewardRsp
(
avatarGuid
,
req
.
getFetterLevel
(),
rewardId
));
}
}
}
Prev
1
2
3
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