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
ae2d1fe4
Commit
ae2d1fe4
authored
Jul 21, 2022
by
github-actions
Committed by
Melledy
Jul 21, 2022
Browse files
Fix whitespace [skip actions]
parent
510d564b
Changes
166
Hide whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/game/tower/TowerSystem.java
View file @
ae2d1fe4
...
...
@@ -16,7 +16,7 @@ import java.util.ArrayList;
import
java.util.List
;
public
class
TowerSystem
extends
BaseGameSystem
{
public
TowerSystem
(
GameServer
server
)
{
super
(
server
);
this
.
load
();
...
...
@@ -24,7 +24,7 @@ public class TowerSystem extends BaseGameSystem {
private
TowerScheduleConfig
towerScheduleConfig
;
public
synchronized
void
load
(){
public
synchronized
void
load
()
{
try
(
Reader
fileReader
=
DataLoader
.
loadReader
(
"TowerSchedule.json"
))
{
towerScheduleConfig
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TowerScheduleConfig
.
class
);
}
catch
(
Exception
e
)
{
...
...
@@ -36,13 +36,13 @@ public class TowerSystem extends BaseGameSystem {
return
towerScheduleConfig
;
}
public
TowerScheduleData
getCurrentTowerScheduleData
(){
public
TowerScheduleData
getCurrentTowerScheduleData
()
{
var
data
=
GameData
.
getTowerScheduleDataMap
().
get
(
towerScheduleConfig
.
getScheduleId
());
if
(
data
==
null
){
if
(
data
==
null
)
{
Grasscutter
.
getLogger
().
error
(
"Could not get current tower schedule data by schedule id {}, please check your resource files"
,
towerScheduleConfig
.
getScheduleId
());
}
return
data
;
}
...
...
@@ -56,29 +56,29 @@ public class TowerSystem extends BaseGameSystem {
return
getCurrentTowerScheduleData
().
getSchedules
().
get
(
0
).
getFloorList
();
}
public
int
getNextFloorId
(
int
floorId
){
public
int
getNextFloorId
(
int
floorId
)
{
var
entranceFloors
=
getCurrentTowerScheduleData
().
getEntranceFloorId
();
var
scheduleFloors
=
getScheduleFloors
();
var
nextId
=
0
;
// find in entrance floors first
for
(
int
i
=
0
;
i
<
entranceFloors
.
size
()-
1
;
i
++){
if
(
floorId
==
entranceFloors
.
get
(
i
)){
for
(
int
i
=
0
;
i
<
entranceFloors
.
size
()-
1
;
i
++)
{
if
(
floorId
==
entranceFloors
.
get
(
i
))
{
nextId
=
entranceFloors
.
get
(
i
+
1
);
}
}
if
(
floorId
==
entranceFloors
.
get
(
entranceFloors
.
size
()-
1
)){
if
(
floorId
==
entranceFloors
.
get
(
entranceFloors
.
size
()-
1
))
{
nextId
=
scheduleFloors
.
get
(
0
);
}
if
(
nextId
!=
0
){
if
(
nextId
!=
0
)
{
return
nextId
;
}
// find in schedule floors
for
(
int
i
=
0
;
i
<
scheduleFloors
.
size
()
-
1
;
i
++){
if
(
floorId
==
scheduleFloors
.
get
(
i
)){
for
(
int
i
=
0
;
i
<
scheduleFloors
.
size
()
-
1
;
i
++)
{
if
(
floorId
==
scheduleFloors
.
get
(
i
))
{
nextId
=
scheduleFloors
.
get
(
i
+
1
);
}
}
return
nextId
;
...
...
src/main/java/emu/grasscutter/game/world/Scene.java
View file @
ae2d1fe4
...
...
@@ -30,423 +30,423 @@ import java.util.concurrent.ConcurrentHashMap;
import
java.util.concurrent.CopyOnWriteArrayList
;
public
class
Scene
{
private
final
World
world
;
private
final
SceneData
sceneData
;
private
final
List
<
Player
>
players
;
private
final
Map
<
Integer
,
GameEntity
>
entities
;
private
final
Set
<
SpawnDataEntry
>
spawnedEntities
;
private
final
Set
<
SpawnDataEntry
>
deadSpawnedEntities
;
private
final
Set
<
SceneBlock
>
loadedBlocks
;
private
Set
<
SpawnDataEntry
.
GridBlockId
>
loadedGridBlocks
;
private
boolean
dontDestroyWhenEmpty
;
private
int
autoCloseTime
;
private
int
time
;
private
SceneScriptManager
scriptManager
;
private
WorldChallenge
challenge
;
private
List
<
DungeonSettleListener
>
dungeonSettleListeners
;
private
DungeonData
dungeonData
;
private
int
prevScene
;
// Id of the previous scene
private
int
prevScenePoint
;
private
Set
<
SceneNpcBornEntry
>
npcBornEntrySet
;
public
Scene
(
World
world
,
SceneData
sceneData
)
{
this
.
world
=
world
;
this
.
sceneData
=
sceneData
;
this
.
players
=
new
CopyOnWriteArrayList
<>();
this
.
entities
=
new
ConcurrentHashMap
<>();
this
.
time
=
8
*
60
;
this
.
prevScene
=
3
;
this
.
spawnedEntities
=
ConcurrentHashMap
.
newKeySet
();
this
.
deadSpawnedEntities
=
ConcurrentHashMap
.
newKeySet
();
this
.
loadedBlocks
=
ConcurrentHashMap
.
newKeySet
();
this
.
loadedGridBlocks
=
new
HashSet
<>();
this
.
npcBornEntrySet
=
ConcurrentHashMap
.
newKeySet
();
this
.
scriptManager
=
new
SceneScriptManager
(
this
);
}
public
int
getId
()
{
return
sceneData
.
getId
();
}
public
World
getWorld
()
{
return
world
;
}
public
SceneData
getSceneData
()
{
return
this
.
sceneData
;
}
public
SceneType
getSceneType
()
{
return
getSceneData
().
getSceneType
();
}
public
List
<
Player
>
getPlayers
()
{
return
players
;
}
public
int
getPlayerCount
()
{
return
this
.
getPlayers
().
size
();
}
public
Map
<
Integer
,
GameEntity
>
getEntities
()
{
return
entities
;
}
public
GameEntity
getEntityById
(
int
id
)
{
return
this
.
entities
.
get
(
id
);
}
public
GameEntity
getEntityByConfigId
(
int
configId
)
{
return
this
.
entities
.
values
().
stream
()
.
filter
(
x
->
x
.
getConfigId
()
==
configId
)
.
findFirst
()
.
orElse
(
null
);
}
/**
* @return the autoCloseTime
*/
public
int
getAutoCloseTime
()
{
return
autoCloseTime
;
}
/**
* @param autoCloseTime the autoCloseTime to set
*/
public
void
setAutoCloseTime
(
int
autoCloseTime
)
{
this
.
autoCloseTime
=
autoCloseTime
;
}
public
int
getTime
()
{
return
time
;
}
public
void
changeTime
(
int
time
)
{
this
.
time
=
time
%
1440
;
}
public
int
getPrevScene
()
{
return
prevScene
;
}
public
void
setPrevScene
(
int
prevScene
)
{
this
.
prevScene
=
prevScene
;
}
public
int
getPrevScenePoint
()
{
return
prevScenePoint
;
}
public
void
setPrevScenePoint
(
int
prevPoint
)
{
this
.
prevScenePoint
=
prevPoint
;
}
public
boolean
dontDestroyWhenEmpty
()
{
return
dontDestroyWhenEmpty
;
}
public
void
setDontDestroyWhenEmpty
(
boolean
dontDestroyWhenEmpty
)
{
this
.
dontDestroyWhenEmpty
=
dontDestroyWhenEmpty
;
}
public
Set
<
SceneBlock
>
getLoadedBlocks
()
{
return
loadedBlocks
;
}
public
Set
<
SpawnDataEntry
>
getSpawnedEntities
()
{
return
spawnedEntities
;
}
public
Set
<
SpawnDataEntry
>
getDeadSpawnedEntities
()
{
return
deadSpawnedEntities
;
}
public
SceneScriptManager
getScriptManager
()
{
return
scriptManager
;
}
public
DungeonData
getDungeonData
()
{
return
dungeonData
;
}
public
void
setDungeonData
(
DungeonData
dungeonData
)
{
if
(
dungeonData
==
null
||
this
.
dungeonData
!=
null
||
this
.
getSceneType
()
!=
SceneType
.
SCENE_DUNGEON
||
dungeonData
.
getSceneId
()
!=
this
.
getId
())
{
return
;
}
this
.
dungeonData
=
dungeonData
;
}
public
WorldChallenge
getChallenge
()
{
return
challenge
;
}
public
void
setChallenge
(
WorldChallenge
challenge
)
{
this
.
challenge
=
challenge
;
}
public
void
addDungeonSettleObserver
(
DungeonSettleListener
dungeonSettleListener
){
if
(
dungeonSettleListeners
==
null
){
dungeonSettleListeners
=
new
ArrayList
<>();
}
dungeonSettleListeners
.
add
(
dungeonSettleListener
);
}
public
List
<
DungeonSettleListener
>
getDungeonSettleObservers
()
{
return
dungeonSettleListeners
;
}
public
boolean
isInScene
(
GameEntity
entity
)
{
return
this
.
entities
.
containsKey
(
entity
.
getId
());
}
public
synchronized
void
addPlayer
(
Player
player
)
{
// Check if player already in
if
(
getPlayers
().
contains
(
player
))
{
return
;
}
// Remove player from prev scene
if
(
player
.
getScene
()
!=
null
)
{
player
.
getScene
().
removePlayer
(
player
);
}
// Add
getPlayers
().
add
(
player
);
player
.
setSceneId
(
this
.
getId
());
player
.
setScene
(
this
);
this
.
setupPlayerAvatars
(
player
);
}
public
synchronized
void
removePlayer
(
Player
player
)
{
// Remove from challenge if leaving
if
(
this
.
getChallenge
()
!=
null
&&
this
.
getChallenge
().
inProgress
())
{
player
.
sendPacket
(
new
PacketDungeonChallengeFinishNotify
(
this
.
getChallenge
()));
}
// Remove player from scene
getPlayers
().
remove
(
player
);
player
.
setScene
(
null
);
// Remove player avatars
this
.
removePlayerAvatars
(
player
);
// Remove player gadgets
for
(
EntityBaseGadget
gadget
:
player
.
getTeamManager
().
getGadgets
())
{
this
.
removeEntity
(
gadget
);
}
// Deregister scene if not in use
if
(
this
.
getPlayerCount
()
<=
0
&&
!
this
.
dontDestroyWhenEmpty
())
{
this
.
getWorld
().
deregisterScene
(
this
);
}
}
private
void
setupPlayerAvatars
(
Player
player
)
{
// Clear entities from old team
player
.
getTeamManager
().
getActiveTeam
().
clear
();
// Add new entities for player
TeamInfo
teamInfo
=
player
.
getTeamManager
().
getCurrentTeamInfo
();
for
(
int
avatarId
:
teamInfo
.
getAvatars
())
{
EntityAvatar
entity
=
new
EntityAvatar
(
player
.
getScene
(),
player
.
getAvatars
().
getAvatarById
(
avatarId
));
player
.
getTeamManager
().
getActiveTeam
().
add
(
entity
);
}
// Limit character index in case its out of bounds
if
(
player
.
getTeamManager
().
getCurrentCharacterIndex
()
>=
player
.
getTeamManager
().
getActiveTeam
().
size
()
||
player
.
getTeamManager
().
getCurrentCharacterIndex
()
<
0
)
{
player
.
getTeamManager
().
setCurrentCharacterIndex
(
player
.
getTeamManager
().
getCurrentCharacterIndex
()
-
1
);
}
}
private
void
removePlayerAvatars
(
Player
player
)
{
Iterator
<
EntityAvatar
>
it
=
player
.
getTeamManager
().
getActiveTeam
().
iterator
();
while
(
it
.
hasNext
())
{
this
.
removeEntity
(
it
.
next
(),
VisionType
.
VISION_TYPE_REMOVE
);
it
.
remove
();
}
}
public
void
spawnPlayer
(
Player
player
)
{
if
(
this
.
isInScene
(
player
.
getTeamManager
().
getCurrentAvatarEntity
()))
{
return
;
}
if
(
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
)
<=
0
f
)
{
player
.
getTeamManager
().
getCurrentAvatarEntity
().
setFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
,
1
f
);
}
this
.
addEntity
(
player
.
getTeamManager
().
getCurrentAvatarEntity
());
// Notify the client of any extra skill charges
for
(
EntityAvatar
entity
:
player
.
getTeamManager
().
getActiveTeam
())
{
if
(
entity
.
getAvatar
().
getSkillExtraChargeMap
().
size
()
>
0
)
{
player
.
sendPacket
(
new
PacketAvatarSkillInfoNotify
(
entity
.
getAvatar
()));
}
}
}
private
void
addEntityDirectly
(
GameEntity
entity
)
{
getEntities
().
put
(
entity
.
getId
(),
entity
);
entity
.
onCreate
();
// Call entity create event
}
public
synchronized
void
addEntity
(
GameEntity
entity
)
{
this
.
addEntityDirectly
(
entity
);
this
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
entity
));
}
public
synchronized
void
addEntityToSingleClient
(
Player
player
,
GameEntity
entity
)
{
this
.
addEntityDirectly
(
entity
);
player
.
sendPacket
(
new
PacketSceneEntityAppearNotify
(
entity
));
}
public
void
addEntities
(
Collection
<?
extends
GameEntity
>
entities
){
addEntities
(
entities
,
VisionType
.
VISION_TYPE_BORN
);
}
public
synchronized
void
addEntities
(
Collection
<?
extends
GameEntity
>
entities
,
VisionType
visionType
)
{
if
(
entities
==
null
||
entities
.
isEmpty
()){
return
;
}
for
(
GameEntity
entity
:
entities
)
{
this
.
addEntityDirectly
(
entity
);
}
this
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
entities
,
visionType
));
}
private
GameEntity
removeEntityDirectly
(
GameEntity
entity
)
{
return
getEntities
().
remove
(
entity
.
getId
());
}
public
void
removeEntity
(
GameEntity
entity
)
{
this
.
removeEntity
(
entity
,
VisionType
.
VISION_TYPE_DIE
);
}
public
synchronized
void
removeEntity
(
GameEntity
entity
,
VisionType
visionType
)
{
GameEntity
removed
=
this
.
removeEntityDirectly
(
entity
);
if
(
removed
!=
null
)
{
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
removed
,
visionType
));
}
}
public
synchronized
void
removeEntities
(
List
<
GameEntity
>
entity
,
VisionType
visionType
)
{
var
toRemove
=
entity
.
stream
()
.
map
(
this
::
removeEntityDirectly
)
.
toList
();
if
(
toRemove
.
size
()
>
0
)
{
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
toRemove
,
visionType
));
}
}
public
synchronized
void
replaceEntity
(
EntityAvatar
oldEntity
,
EntityAvatar
newEntity
)
{
this
.
removeEntityDirectly
(
oldEntity
);
this
.
addEntityDirectly
(
newEntity
);
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
oldEntity
,
VisionType
.
VISION_TYPE_REPLACE
));
this
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
newEntity
,
VisionType
.
VISION_TYPE_REPLACE
,
oldEntity
.
getId
()));
}
public
void
showOtherEntities
(
Player
player
)
{
List
<
GameEntity
>
entities
=
new
LinkedList
<>();
GameEntity
currentEntity
=
player
.
getTeamManager
().
getCurrentAvatarEntity
();
for
(
GameEntity
entity
:
this
.
getEntities
().
values
())
{
if
(
entity
==
currentEntity
)
{
continue
;
}
entities
.
add
(
entity
);
}
player
.
sendPacket
(
new
PacketSceneEntityAppearNotify
(
entities
,
VisionType
.
VISION_TYPE_MEET
));
}
public
void
handleAttack
(
AttackResult
result
)
{
//GameEntity attacker = getEntityById(result.getAttackerId());
GameEntity
target
=
getEntityById
(
result
.
getDefenseId
());
if
(
target
==
null
)
{
return
;
}
// Godmode check
if
(
target
instanceof
EntityAvatar
)
{
if
(((
EntityAvatar
)
target
).
getPlayer
().
inGodmode
())
{
return
;
}
}
// Sanity check
target
.
damage
(
result
.
getDamage
(),
result
.
getAttackerId
());
}
public
void
killEntity
(
GameEntity
target
)
{
killEntity
(
target
,
0
);
}
public
void
killEntity
(
GameEntity
target
,
int
attackerId
)
{
GameEntity
attacker
=
null
;
if
(
attackerId
>
0
)
{
attacker
=
getEntityById
(
attackerId
);
}
if
(
attacker
!=
null
)
{
// Check codex
if
(
attacker
instanceof
EntityClientGadget
gadgetAttacker
)
{
var
clientGadgetOwner
=
getEntityById
(
gadgetAttacker
.
getOwnerEntityId
());
if
(
clientGadgetOwner
instanceof
EntityAvatar
)
{
((
EntityClientGadget
)
attacker
).
getOwner
().
getCodex
().
checkAnimal
(
target
,
CodexAnimalData
.
CodexAnimalUnlockCondition
.
CODEX_COUNT_TYPE_KILL
);
}
}
else
if
(
attacker
instanceof
EntityAvatar
avatarAttacker
)
{
avatarAttacker
.
getPlayer
().
getCodex
().
checkAnimal
(
target
,
CodexAnimalData
.
CodexAnimalUnlockCondition
.
CODEX_COUNT_TYPE_KILL
);
}
}
// Packet
this
.
broadcastPacket
(
new
PacketLifeStateChangeNotify
(
attackerId
,
target
,
LifeState
.
LIFE_DEAD
));
// Reward drop
if
(
target
instanceof
EntityMonster
&&
this
.
getSceneType
()
!=
SceneType
.
SCENE_DUNGEON
)
{
getWorld
().
getServer
().
getDropSystem
().
callDrop
((
EntityMonster
)
target
);
}
// Remove entity from world
this
.
removeEntity
(
target
);
// Death event
target
.
onDeath
(
attackerId
);
}
public
void
onTick
()
{
// disable script for home
if
(
this
.
getSceneType
()
==
SceneType
.
SCENE_HOME_WORLD
||
this
.
getSceneType
()
==
SceneType
.
SCENE_HOME_ROOM
){
return
;
}
if
(
this
.
getScriptManager
().
isInit
())
{
this
.
checkBlocks
();
}
else
{
// TEMPORARY
this
.
checkSpawns
();
}
// Triggers
this
.
scriptManager
.
checkRegions
();
if
(
challenge
!=
null
){
challenge
.
onCheckTimeOut
();
}
private
final
World
world
;
private
final
SceneData
sceneData
;
private
final
List
<
Player
>
players
;
private
final
Map
<
Integer
,
GameEntity
>
entities
;
private
final
Set
<
SpawnDataEntry
>
spawnedEntities
;
private
final
Set
<
SpawnDataEntry
>
deadSpawnedEntities
;
private
final
Set
<
SceneBlock
>
loadedBlocks
;
private
Set
<
SpawnDataEntry
.
GridBlockId
>
loadedGridBlocks
;
private
boolean
dontDestroyWhenEmpty
;
private
int
autoCloseTime
;
private
int
time
;
private
SceneScriptManager
scriptManager
;
private
WorldChallenge
challenge
;
private
List
<
DungeonSettleListener
>
dungeonSettleListeners
;
private
DungeonData
dungeonData
;
private
int
prevScene
;
// Id of the previous scene
private
int
prevScenePoint
;
private
Set
<
SceneNpcBornEntry
>
npcBornEntrySet
;
public
Scene
(
World
world
,
SceneData
sceneData
)
{
this
.
world
=
world
;
this
.
sceneData
=
sceneData
;
this
.
players
=
new
CopyOnWriteArrayList
<>();
this
.
entities
=
new
ConcurrentHashMap
<>();
this
.
time
=
8
*
60
;
this
.
prevScene
=
3
;
this
.
spawnedEntities
=
ConcurrentHashMap
.
newKeySet
();
this
.
deadSpawnedEntities
=
ConcurrentHashMap
.
newKeySet
();
this
.
loadedBlocks
=
ConcurrentHashMap
.
newKeySet
();
this
.
loadedGridBlocks
=
new
HashSet
<>();
this
.
npcBornEntrySet
=
ConcurrentHashMap
.
newKeySet
();
this
.
scriptManager
=
new
SceneScriptManager
(
this
);
}
public
int
getId
()
{
return
sceneData
.
getId
();
}
public
World
getWorld
()
{
return
world
;
}
public
SceneData
getSceneData
()
{
return
this
.
sceneData
;
}
public
SceneType
getSceneType
()
{
return
getSceneData
().
getSceneType
();
}
public
List
<
Player
>
getPlayers
()
{
return
players
;
}
public
int
getPlayerCount
()
{
return
this
.
getPlayers
().
size
();
}
public
Map
<
Integer
,
GameEntity
>
getEntities
()
{
return
entities
;
}
public
GameEntity
getEntityById
(
int
id
)
{
return
this
.
entities
.
get
(
id
);
}
public
GameEntity
getEntityByConfigId
(
int
configId
)
{
return
this
.
entities
.
values
().
stream
()
.
filter
(
x
->
x
.
getConfigId
()
==
configId
)
.
findFirst
()
.
orElse
(
null
);
}
/**
* @return the autoCloseTime
*/
public
int
getAutoCloseTime
()
{
return
autoCloseTime
;
}
/**
* @param autoCloseTime the autoCloseTime to set
*/
public
void
setAutoCloseTime
(
int
autoCloseTime
)
{
this
.
autoCloseTime
=
autoCloseTime
;
}
public
int
getTime
()
{
return
time
;
}
public
void
changeTime
(
int
time
)
{
this
.
time
=
time
%
1440
;
}
public
int
getPrevScene
()
{
return
prevScene
;
}
public
void
setPrevScene
(
int
prevScene
)
{
this
.
prevScene
=
prevScene
;
}
public
int
getPrevScenePoint
()
{
return
prevScenePoint
;
}
public
void
setPrevScenePoint
(
int
prevPoint
)
{
this
.
prevScenePoint
=
prevPoint
;
}
public
boolean
dontDestroyWhenEmpty
()
{
return
dontDestroyWhenEmpty
;
}
public
void
setDontDestroyWhenEmpty
(
boolean
dontDestroyWhenEmpty
)
{
this
.
dontDestroyWhenEmpty
=
dontDestroyWhenEmpty
;
}
public
Set
<
SceneBlock
>
getLoadedBlocks
()
{
return
loadedBlocks
;
}
public
Set
<
SpawnDataEntry
>
getSpawnedEntities
()
{
return
spawnedEntities
;
}
public
Set
<
SpawnDataEntry
>
getDeadSpawnedEntities
()
{
return
deadSpawnedEntities
;
}
public
SceneScriptManager
getScriptManager
()
{
return
scriptManager
;
}
public
DungeonData
getDungeonData
()
{
return
dungeonData
;
}
public
void
setDungeonData
(
DungeonData
dungeonData
)
{
if
(
dungeonData
==
null
||
this
.
dungeonData
!=
null
||
this
.
getSceneType
()
!=
SceneType
.
SCENE_DUNGEON
||
dungeonData
.
getSceneId
()
!=
this
.
getId
())
{
return
;
}
this
.
dungeonData
=
dungeonData
;
}
public
WorldChallenge
getChallenge
()
{
return
challenge
;
}
public
void
setChallenge
(
WorldChallenge
challenge
)
{
this
.
challenge
=
challenge
;
}
public
void
addDungeonSettleObserver
(
DungeonSettleListener
dungeonSettleListener
)
{
if
(
dungeonSettleListeners
==
null
)
{
dungeonSettleListeners
=
new
ArrayList
<>();
}
dungeonSettleListeners
.
add
(
dungeonSettleListener
);
}
public
List
<
DungeonSettleListener
>
getDungeonSettleObservers
()
{
return
dungeonSettleListeners
;
}
public
boolean
isInScene
(
GameEntity
entity
)
{
return
this
.
entities
.
containsKey
(
entity
.
getId
());
}
public
synchronized
void
addPlayer
(
Player
player
)
{
// Check if player already in
if
(
getPlayers
().
contains
(
player
))
{
return
;
}
// Remove player from prev scene
if
(
player
.
getScene
()
!=
null
)
{
player
.
getScene
().
removePlayer
(
player
);
}
// Add
getPlayers
().
add
(
player
);
player
.
setSceneId
(
this
.
getId
());
player
.
setScene
(
this
);
this
.
setupPlayerAvatars
(
player
);
}
public
synchronized
void
removePlayer
(
Player
player
)
{
// Remove from challenge if leaving
if
(
this
.
getChallenge
()
!=
null
&&
this
.
getChallenge
().
inProgress
())
{
player
.
sendPacket
(
new
PacketDungeonChallengeFinishNotify
(
this
.
getChallenge
()));
}
// Remove player from scene
getPlayers
().
remove
(
player
);
player
.
setScene
(
null
);
// Remove player avatars
this
.
removePlayerAvatars
(
player
);
// Remove player gadgets
for
(
EntityBaseGadget
gadget
:
player
.
getTeamManager
().
getGadgets
())
{
this
.
removeEntity
(
gadget
);
}
// Deregister scene if not in use
if
(
this
.
getPlayerCount
()
<=
0
&&
!
this
.
dontDestroyWhenEmpty
())
{
this
.
getWorld
().
deregisterScene
(
this
);
}
}
private
void
setupPlayerAvatars
(
Player
player
)
{
// Clear entities from old team
player
.
getTeamManager
().
getActiveTeam
().
clear
();
// Add new entities for player
TeamInfo
teamInfo
=
player
.
getTeamManager
().
getCurrentTeamInfo
();
for
(
int
avatarId
:
teamInfo
.
getAvatars
())
{
EntityAvatar
entity
=
new
EntityAvatar
(
player
.
getScene
(),
player
.
getAvatars
().
getAvatarById
(
avatarId
));
player
.
getTeamManager
().
getActiveTeam
().
add
(
entity
);
}
// Limit character index in case its out of bounds
if
(
player
.
getTeamManager
().
getCurrentCharacterIndex
()
>=
player
.
getTeamManager
().
getActiveTeam
().
size
()
||
player
.
getTeamManager
().
getCurrentCharacterIndex
()
<
0
)
{
player
.
getTeamManager
().
setCurrentCharacterIndex
(
player
.
getTeamManager
().
getCurrentCharacterIndex
()
-
1
);
}
}
private
void
removePlayerAvatars
(
Player
player
)
{
Iterator
<
EntityAvatar
>
it
=
player
.
getTeamManager
().
getActiveTeam
().
iterator
();
while
(
it
.
hasNext
())
{
this
.
removeEntity
(
it
.
next
(),
VisionType
.
VISION_TYPE_REMOVE
);
it
.
remove
();
}
}
public
void
spawnPlayer
(
Player
player
)
{
if
(
this
.
isInScene
(
player
.
getTeamManager
().
getCurrentAvatarEntity
()))
{
return
;
}
if
(
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
)
<=
0
f
)
{
player
.
getTeamManager
().
getCurrentAvatarEntity
().
setFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
,
1
f
);
}
this
.
addEntity
(
player
.
getTeamManager
().
getCurrentAvatarEntity
());
// Notify the client of any extra skill charges
for
(
EntityAvatar
entity
:
player
.
getTeamManager
().
getActiveTeam
())
{
if
(
entity
.
getAvatar
().
getSkillExtraChargeMap
().
size
()
>
0
)
{
player
.
sendPacket
(
new
PacketAvatarSkillInfoNotify
(
entity
.
getAvatar
()));
}
}
}
private
void
addEntityDirectly
(
GameEntity
entity
)
{
getEntities
().
put
(
entity
.
getId
(),
entity
);
entity
.
onCreate
();
// Call entity create event
}
public
synchronized
void
addEntity
(
GameEntity
entity
)
{
this
.
addEntityDirectly
(
entity
);
this
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
entity
));
}
public
synchronized
void
addEntityToSingleClient
(
Player
player
,
GameEntity
entity
)
{
this
.
addEntityDirectly
(
entity
);
player
.
sendPacket
(
new
PacketSceneEntityAppearNotify
(
entity
));
}
public
void
addEntities
(
Collection
<?
extends
GameEntity
>
entities
)
{
addEntities
(
entities
,
VisionType
.
VISION_TYPE_BORN
);
}
public
synchronized
void
addEntities
(
Collection
<?
extends
GameEntity
>
entities
,
VisionType
visionType
)
{
if
(
entities
==
null
||
entities
.
isEmpty
())
{
return
;
}
for
(
GameEntity
entity
:
entities
)
{
this
.
addEntityDirectly
(
entity
);
}
this
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
entities
,
visionType
));
}
private
GameEntity
removeEntityDirectly
(
GameEntity
entity
)
{
return
getEntities
().
remove
(
entity
.
getId
());
}
public
void
removeEntity
(
GameEntity
entity
)
{
this
.
removeEntity
(
entity
,
VisionType
.
VISION_TYPE_DIE
);
}
public
synchronized
void
removeEntity
(
GameEntity
entity
,
VisionType
visionType
)
{
GameEntity
removed
=
this
.
removeEntityDirectly
(
entity
);
if
(
removed
!=
null
)
{
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
removed
,
visionType
));
}
}
public
synchronized
void
removeEntities
(
List
<
GameEntity
>
entity
,
VisionType
visionType
)
{
var
toRemove
=
entity
.
stream
()
.
map
(
this
::
removeEntityDirectly
)
.
toList
();
if
(
toRemove
.
size
()
>
0
)
{
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
toRemove
,
visionType
));
}
}
public
synchronized
void
replaceEntity
(
EntityAvatar
oldEntity
,
EntityAvatar
newEntity
)
{
this
.
removeEntityDirectly
(
oldEntity
);
this
.
addEntityDirectly
(
newEntity
);
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
oldEntity
,
VisionType
.
VISION_TYPE_REPLACE
));
this
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
newEntity
,
VisionType
.
VISION_TYPE_REPLACE
,
oldEntity
.
getId
()));
}
public
void
showOtherEntities
(
Player
player
)
{
List
<
GameEntity
>
entities
=
new
LinkedList
<>();
GameEntity
currentEntity
=
player
.
getTeamManager
().
getCurrentAvatarEntity
();
for
(
GameEntity
entity
:
this
.
getEntities
().
values
())
{
if
(
entity
==
currentEntity
)
{
continue
;
}
entities
.
add
(
entity
);
}
player
.
sendPacket
(
new
PacketSceneEntityAppearNotify
(
entities
,
VisionType
.
VISION_TYPE_MEET
));
}
public
void
handleAttack
(
AttackResult
result
)
{
//GameEntity attacker = getEntityById(result.getAttackerId());
GameEntity
target
=
getEntityById
(
result
.
getDefenseId
());
if
(
target
==
null
)
{
return
;
}
// Godmode check
if
(
target
instanceof
EntityAvatar
)
{
if
(((
EntityAvatar
)
target
).
getPlayer
().
inGodmode
())
{
return
;
}
}
// Sanity check
target
.
damage
(
result
.
getDamage
(),
result
.
getAttackerId
());
}
public
void
killEntity
(
GameEntity
target
)
{
killEntity
(
target
,
0
);
}
public
void
killEntity
(
GameEntity
target
,
int
attackerId
)
{
GameEntity
attacker
=
null
;
if
(
attackerId
>
0
)
{
attacker
=
getEntityById
(
attackerId
);
}
if
(
attacker
!=
null
)
{
// Check codex
if
(
attacker
instanceof
EntityClientGadget
gadgetAttacker
)
{
var
clientGadgetOwner
=
getEntityById
(
gadgetAttacker
.
getOwnerEntityId
());
if
(
clientGadgetOwner
instanceof
EntityAvatar
)
{
((
EntityClientGadget
)
attacker
).
getOwner
().
getCodex
().
checkAnimal
(
target
,
CodexAnimalData
.
CodexAnimalUnlockCondition
.
CODEX_COUNT_TYPE_KILL
);
}
}
else
if
(
attacker
instanceof
EntityAvatar
avatarAttacker
)
{
avatarAttacker
.
getPlayer
().
getCodex
().
checkAnimal
(
target
,
CodexAnimalData
.
CodexAnimalUnlockCondition
.
CODEX_COUNT_TYPE_KILL
);
}
}
// Packet
this
.
broadcastPacket
(
new
PacketLifeStateChangeNotify
(
attackerId
,
target
,
LifeState
.
LIFE_DEAD
));
// Reward drop
if
(
target
instanceof
EntityMonster
&&
this
.
getSceneType
()
!=
SceneType
.
SCENE_DUNGEON
)
{
getWorld
().
getServer
().
getDropSystem
().
callDrop
((
EntityMonster
)
target
);
}
// Remove entity from world
this
.
removeEntity
(
target
);
// Death event
target
.
onDeath
(
attackerId
);
}
public
void
onTick
()
{
// disable script for home
if
(
this
.
getSceneType
()
==
SceneType
.
SCENE_HOME_WORLD
||
this
.
getSceneType
()
==
SceneType
.
SCENE_HOME_ROOM
)
{
return
;
}
if
(
this
.
getScriptManager
().
isInit
())
{
this
.
checkBlocks
();
}
else
{
// TEMPORARY
this
.
checkSpawns
();
}
// Triggers
this
.
scriptManager
.
checkRegions
();
if
(
challenge
!=
null
)
{
challenge
.
onCheckTimeOut
();
}
checkNpcGroup
();
}
}
public
int
getEntityLevel
(
int
baseLevel
,
int
worldLevelOverride
)
{
int
level
=
worldLevelOverride
>
0
?
worldLevelOverride
+
baseLevel
-
22
:
baseLevel
;
level
=
level
>=
100
?
100
:
level
;
level
=
level
<=
0
?
1
:
level
;
public
int
getEntityLevel
(
int
baseLevel
,
int
worldLevelOverride
)
{
int
level
=
worldLevelOverride
>
0
?
worldLevelOverride
+
baseLevel
-
22
:
baseLevel
;
level
=
level
>=
100
?
100
:
level
;
level
=
level
<=
0
?
1
:
level
;
return
level
;
}
public
void
checkNpcGroup
(){
return
level
;
}
public
void
checkNpcGroup
()
{
Set
<
SceneNpcBornEntry
>
npcBornEntries
=
ConcurrentHashMap
.
newKeySet
();
for
(
Player
player
:
this
.
getPlayers
())
{
npcBornEntries
.
addAll
(
loadNpcForPlayer
(
player
));
...
...
@@ -458,7 +458,7 @@ public class Scene {
.
map
(
SceneNpcBornEntry:
:
getGroupId
)
.
toList
();
if
(
toUnload
.
size
()
>
0
){
if
(
toUnload
.
size
()
>
0
)
{
broadcastPacket
(
new
PacketGroupUnloadNotify
(
toUnload
));
Grasscutter
.
getLogger
().
debug
(
"Unload NPC Group {}"
,
toUnload
);
}
...
...
@@ -480,342 +480,342 @@ public class Scene {
Set
<
SpawnDataEntry
>
visible
=
new
HashSet
<>();
for
(
var
block
:
loadedGridBlocks
)
{
var
spawns
=
spawnLists
.
get
(
block
);
if
(
spawns
!=
null
)
{
if
(
spawns
!=
null
)
{
visible
.
addAll
(
spawns
);
}
}
// World level
WorldLevelData
worldLevelData
=
GameData
.
getWorldLevelDataMap
().
get
(
getWorld
().
getWorldLevel
());
int
worldLevelOverride
=
0
;
// World level
WorldLevelData
worldLevelData
=
GameData
.
getWorldLevelDataMap
().
get
(
getWorld
().
getWorldLevel
());
int
worldLevelOverride
=
0
;
if
(
worldLevelData
!=
null
)
{
worldLevelOverride
=
worldLevelData
.
getMonsterLevel
();
}
if
(
worldLevelData
!=
null
)
{
worldLevelOverride
=
worldLevelData
.
getMonsterLevel
();
}
// Todo
List
<
GameEntity
>
toAdd
=
new
LinkedList
<>();
List
<
GameEntity
>
toRemove
=
new
LinkedList
<>();
// Todo
List
<
GameEntity
>
toAdd
=
new
LinkedList
<>();
List
<
GameEntity
>
toRemove
=
new
LinkedList
<>();
var
spawnedEntities
=
this
.
getSpawnedEntities
();
for
(
SpawnDataEntry
entry
:
visible
)
{
// If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it
if
(!
spawnedEntities
.
contains
(
entry
)
&&
!
this
.
getDeadSpawnedEntities
().
contains
(
entry
))
{
// Entity object holder
GameEntity
entity
=
null
;
// Check if spawn entry is monster or gadget
if
(
entry
.
getMonsterId
()
>
0
)
{
MonsterData
data
=
GameData
.
getMonsterDataMap
().
get
(
entry
.
getMonsterId
());
if
(
data
==
null
)
continue
;
int
level
=
this
.
getEntityLevel
(
entry
.
getLevel
(),
worldLevelOverride
);
EntityMonster
monster
=
new
EntityMonster
(
this
,
data
,
entry
.
getPos
(),
level
);
monster
.
getRotation
().
set
(
entry
.
getRot
());
monster
.
setGroupId
(
entry
.
getGroup
().
getGroupId
());
monster
.
setPoseId
(
entry
.
getPoseId
());
monster
.
setConfigId
(
entry
.
getConfigId
());
monster
.
setSpawnEntry
(
entry
);
entity
=
monster
;
}
else
if
(
entry
.
getGadgetId
()
>
0
)
{
EntityGadget
gadget
=
new
EntityGadget
(
this
,
entry
.
getGadgetId
(),
entry
.
getPos
(),
entry
.
getRot
());
gadget
.
setGroupId
(
entry
.
getGroup
().
getGroupId
());
gadget
.
setConfigId
(
entry
.
getConfigId
());
gadget
.
setSpawnEntry
(
entry
);
int
state
=
entry
.
getGadgetState
();
if
(
state
>
0
)
{
for
(
SpawnDataEntry
entry
:
visible
)
{
// If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it
if
(!
spawnedEntities
.
contains
(
entry
)
&&
!
this
.
getDeadSpawnedEntities
().
contains
(
entry
))
{
// Entity object holder
GameEntity
entity
=
null
;
// Check if spawn entry is monster or gadget
if
(
entry
.
getMonsterId
()
>
0
)
{
MonsterData
data
=
GameData
.
getMonsterDataMap
().
get
(
entry
.
getMonsterId
());
if
(
data
==
null
)
continue
;
int
level
=
this
.
getEntityLevel
(
entry
.
getLevel
(),
worldLevelOverride
);
EntityMonster
monster
=
new
EntityMonster
(
this
,
data
,
entry
.
getPos
(),
level
);
monster
.
getRotation
().
set
(
entry
.
getRot
());
monster
.
setGroupId
(
entry
.
getGroup
().
getGroupId
());
monster
.
setPoseId
(
entry
.
getPoseId
());
monster
.
setConfigId
(
entry
.
getConfigId
());
monster
.
setSpawnEntry
(
entry
);
entity
=
monster
;
}
else
if
(
entry
.
getGadgetId
()
>
0
)
{
EntityGadget
gadget
=
new
EntityGadget
(
this
,
entry
.
getGadgetId
(),
entry
.
getPos
(),
entry
.
getRot
());
gadget
.
setGroupId
(
entry
.
getGroup
().
getGroupId
());
gadget
.
setConfigId
(
entry
.
getConfigId
());
gadget
.
setSpawnEntry
(
entry
);
int
state
=
entry
.
getGadgetState
();
if
(
state
>
0
)
{
gadget
.
setState
(
state
);
}
gadget
.
buildContent
();
gadget
.
buildContent
();
gadget
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_BASE_HP
,
99999
);
gadget
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
,
99999
);
gadget
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
,
99999
);
gadget
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_BASE_HP
,
99999
);
gadget
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
,
99999
);
gadget
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
,
99999
);
entity
=
gadget
;
}
entity
=
gadget
;
}
if
(
entity
==
null
)
continue
;
if
(
entity
==
null
)
continue
;
// Add to scene and spawned list
toAdd
.
add
(
entity
);
// Add to scene and spawned list
toAdd
.
add
(
entity
);
spawnedEntities
.
add
(
entry
);
}
}
}
}
for
(
GameEntity
entity
:
this
.
getEntities
().
values
())
{
var
spawnEntry
=
entity
.
getSpawnEntry
();
if
(
spawnEntry
!=
null
&&
!
visible
.
contains
(
spawnEntry
))
{
toRemove
.
add
(
entity
);
for
(
GameEntity
entity
:
this
.
getEntities
().
values
())
{
var
spawnEntry
=
entity
.
getSpawnEntry
();
if
(
spawnEntry
!=
null
&&
!
visible
.
contains
(
spawnEntry
))
{
toRemove
.
add
(
entity
);
spawnedEntities
.
remove
(
spawnEntry
);
}
}
if
(
toAdd
.
size
()
>
0
)
{
toAdd
.
stream
().
forEach
(
this
::
addEntityDirectly
);
this
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
toAdd
,
VisionType
.
VISION_TYPE_BORN
));
}
if
(
toRemove
.
size
()
>
0
)
{
toRemove
.
stream
().
forEach
(
this
::
removeEntityDirectly
);
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
toRemove
,
VisionType
.
VISION_TYPE_REMOVE
));
}
}
public
List
<
SceneBlock
>
getPlayerActiveBlocks
(
Player
player
){
// consider the borders' entities of blocks, so we check if contains by index
return
SceneIndexManager
.
queryNeighbors
(
getScriptManager
().
getBlocksIndex
(),
player
.
getPosition
().
toXZDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
}
public
void
checkBlocks
()
{
Set
<
SceneBlock
>
visible
=
new
HashSet
<>();
for
(
Player
player
:
this
.
getPlayers
())
{
var
blocks
=
getPlayerActiveBlocks
(
player
);
visible
.
addAll
(
blocks
);
}
Iterator
<
SceneBlock
>
it
=
this
.
getLoadedBlocks
().
iterator
();
while
(
it
.
hasNext
())
{
SceneBlock
block
=
it
.
next
();
if
(!
visible
.
contains
(
block
))
{
it
.
remove
();
onUnloadBlock
(
block
);
}
}
for
(
var
block
:
visible
){
if
(!
this
.
getLoadedBlocks
().
contains
(
block
))
{
this
.
onLoadBlock
(
block
,
this
.
getPlayers
());
this
.
getLoadedBlocks
().
add
(
block
);
}
else
{
// dynamic load the groups for players in a loaded block
var
toLoad
=
this
.
getPlayers
().
stream
()
.
filter
(
p
->
block
.
contains
(
p
.
getPosition
()))
.
map
(
p
->
playerMeetGroups
(
p
,
block
))
.
flatMap
(
Collection:
:
stream
)
.
toList
();
onLoadGroup
(
toLoad
);
}
}
}
public
List
<
SceneGroup
>
playerMeetGroups
(
Player
player
,
SceneBlock
block
){
List
<
SceneGroup
>
sceneGroups
=
SceneIndexManager
.
queryNeighbors
(
block
.
sceneGroupIndex
,
player
.
getPosition
().
toDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
List
<
SceneGroup
>
groups
=
sceneGroups
.
stream
()
.
filter
(
group
->
!
scriptManager
.
getLoadedGroupSetPerBlock
().
get
(
block
.
id
).
contains
(
group
))
.
peek
(
group
->
scriptManager
.
getLoadedGroupSetPerBlock
().
get
(
block
.
id
).
add
(
group
))
.
toList
();
if
(
groups
.
size
()
==
0
)
{
return
List
.
of
();
}
return
groups
;
}
public
void
onLoadBlock
(
SceneBlock
block
,
List
<
Player
>
players
)
{
this
.
getScriptManager
().
loadBlockFromScript
(
block
);
scriptManager
.
getLoadedGroupSetPerBlock
().
put
(
block
.
id
,
new
HashSet
<>());
// the groups form here is not added in current scene
var
groups
=
players
.
stream
()
.
filter
(
player
->
block
.
contains
(
player
.
getPosition
()))
.
map
(
p
->
playerMeetGroups
(
p
,
block
))
.
flatMap
(
Collection:
:
stream
)
.
toList
();
onLoadGroup
(
groups
);
Grasscutter
.
getLogger
().
info
(
"Scene {} Block {} loaded."
,
this
.
getId
(),
block
.
id
);
}
public
void
onLoadGroup
(
List
<
SceneGroup
>
groups
){
if
(
groups
==
null
||
groups
.
isEmpty
()){
return
;
}
for
(
SceneGroup
group
:
groups
)
{
// We load the script files for the groups here
this
.
getScriptManager
().
loadGroupFromScript
(
group
);
}
// Spawn gadgets AFTER triggers are added
// TODO
var
entities
=
new
ArrayList
<
GameEntity
>();
for
(
SceneGroup
group
:
groups
)
{
if
(
group
.
init_config
==
null
)
{
continue
;
}
// Load garbages
List
<
SceneGadget
>
garbageGadgets
=
group
.
getGarbageGadgets
();
if
(
garbageGadgets
!=
null
)
{
entities
.
addAll
(
garbageGadgets
.
stream
().
map
(
g
->
scriptManager
.
createGadget
(
group
.
id
,
group
.
block_id
,
g
))
.
filter
(
Objects:
:
nonNull
)
.
toList
());
}
// Load suites
int
suite
=
group
.
init_config
.
suite
;
if
(
suite
==
0
||
group
.
suites
==
null
||
group
.
suites
.
size
()
==
0
)
{
continue
;
}
// just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc.
var
suiteData
=
group
.
getSuiteByIndex
(
suite
);
suiteData
.
sceneTriggers
.
forEach
(
getScriptManager
()::
registerTrigger
);
entities
.
addAll
(
scriptManager
.
getGadgetsInGroupSuite
(
group
,
suiteData
));
entities
.
addAll
(
scriptManager
.
getMonstersInGroupSuite
(
group
,
suiteData
));
}
}
if
(
toAdd
.
size
()
>
0
)
{
toAdd
.
stream
().
forEach
(
this
::
addEntityDirectly
);
this
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
toAdd
,
VisionType
.
VISION_TYPE_BORN
));
}
if
(
toRemove
.
size
()
>
0
)
{
toRemove
.
stream
().
forEach
(
this
::
removeEntityDirectly
);
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
toRemove
,
VisionType
.
VISION_TYPE_REMOVE
));
}
}
public
List
<
SceneBlock
>
getPlayerActiveBlocks
(
Player
player
)
{
// consider the borders' entities of blocks, so we check if contains by index
return
SceneIndexManager
.
queryNeighbors
(
getScriptManager
().
getBlocksIndex
(),
player
.
getPosition
().
toXZDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
}
public
void
checkBlocks
()
{
Set
<
SceneBlock
>
visible
=
new
HashSet
<>();
for
(
Player
player
:
this
.
getPlayers
())
{
var
blocks
=
getPlayerActiveBlocks
(
player
);
visible
.
addAll
(
blocks
);
}
Iterator
<
SceneBlock
>
it
=
this
.
getLoadedBlocks
().
iterator
();
while
(
it
.
hasNext
())
{
SceneBlock
block
=
it
.
next
();
if
(!
visible
.
contains
(
block
))
{
it
.
remove
();
onUnloadBlock
(
block
);
}
}
for
(
var
block
:
visible
)
{
if
(!
this
.
getLoadedBlocks
().
contains
(
block
))
{
this
.
onLoadBlock
(
block
,
this
.
getPlayers
());
this
.
getLoadedBlocks
().
add
(
block
);
}
else
{
// dynamic load the groups for players in a loaded block
var
toLoad
=
this
.
getPlayers
().
stream
()
.
filter
(
p
->
block
.
contains
(
p
.
getPosition
()))
.
map
(
p
->
playerMeetGroups
(
p
,
block
))
.
flatMap
(
Collection:
:
stream
)
.
toList
();
onLoadGroup
(
toLoad
);
}
}
}
public
List
<
SceneGroup
>
playerMeetGroups
(
Player
player
,
SceneBlock
block
)
{
List
<
SceneGroup
>
sceneGroups
=
SceneIndexManager
.
queryNeighbors
(
block
.
sceneGroupIndex
,
player
.
getPosition
().
toDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
List
<
SceneGroup
>
groups
=
sceneGroups
.
stream
()
.
filter
(
group
->
!
scriptManager
.
getLoadedGroupSetPerBlock
().
get
(
block
.
id
).
contains
(
group
))
.
peek
(
group
->
scriptManager
.
getLoadedGroupSetPerBlock
().
get
(
block
.
id
).
add
(
group
))
.
toList
();
if
(
groups
.
size
()
==
0
)
{
return
List
.
of
();
}
return
groups
;
}
public
void
onLoadBlock
(
SceneBlock
block
,
List
<
Player
>
players
)
{
this
.
getScriptManager
().
loadBlockFromScript
(
block
);
scriptManager
.
getLoadedGroupSetPerBlock
().
put
(
block
.
id
,
new
HashSet
<>());
// the groups form here is not added in current scene
var
groups
=
players
.
stream
()
.
filter
(
player
->
block
.
contains
(
player
.
getPosition
()))
.
map
(
p
->
playerMeetGroups
(
p
,
block
))
.
flatMap
(
Collection:
:
stream
)
.
toList
();
onLoadGroup
(
groups
);
Grasscutter
.
getLogger
().
info
(
"Scene {} Block {} loaded."
,
this
.
getId
(),
block
.
id
);
}
public
void
onLoadGroup
(
List
<
SceneGroup
>
groups
)
{
if
(
groups
==
null
||
groups
.
isEmpty
())
{
return
;
}
for
(
SceneGroup
group
:
groups
)
{
// We load the script files for the groups here
this
.
getScriptManager
().
loadGroupFromScript
(
group
);
}
// Spawn gadgets AFTER triggers are added
// TODO
var
entities
=
new
ArrayList
<
GameEntity
>();
for
(
SceneGroup
group
:
groups
)
{
if
(
group
.
init_config
==
null
)
{
continue
;
}
// Load garbages
List
<
SceneGadget
>
garbageGadgets
=
group
.
getGarbageGadgets
();
if
(
garbageGadgets
!=
null
)
{
entities
.
addAll
(
garbageGadgets
.
stream
().
map
(
g
->
scriptManager
.
createGadget
(
group
.
id
,
group
.
block_id
,
g
))
.
filter
(
Objects:
:
nonNull
)
.
toList
());
}
// Load suites
int
suite
=
group
.
init_config
.
suite
;
if
(
suite
==
0
||
group
.
suites
==
null
||
group
.
suites
.
size
()
==
0
)
{
continue
;
}
// just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc.
var
suiteData
=
group
.
getSuiteByIndex
(
suite
);
suiteData
.
sceneTriggers
.
forEach
(
getScriptManager
()::
registerTrigger
);
entities
.
addAll
(
scriptManager
.
getGadgetsInGroupSuite
(
group
,
suiteData
));
entities
.
addAll
(
scriptManager
.
getMonstersInGroupSuite
(
group
,
suiteData
));
scriptManager
.
registerRegionInGroupSuite
(
group
,
suiteData
);
}
scriptManager
.
meetEntities
(
entities
);
//scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null);
//groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null));
Grasscutter
.
getLogger
().
info
(
"Scene {} loaded {} group(s)"
,
this
.
getId
(),
groups
.
size
());
}
public
void
onUnloadBlock
(
SceneBlock
block
)
{
List
<
GameEntity
>
toRemove
=
this
.
getEntities
().
values
().
stream
()
.
filter
(
e
->
e
.
getBlockId
()
==
block
.
id
).
toList
();
if
(
toRemove
.
size
()
>
0
)
{
toRemove
.
forEach
(
this
::
removeEntityDirectly
);
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
toRemove
,
VisionType
.
VISION_TYPE_REMOVE
));
}
for
(
SceneGroup
group
:
block
.
groups
.
values
())
{
if
(
group
.
triggers
!=
null
){
group
.
triggers
.
values
().
forEach
(
getScriptManager
()::
deregisterTrigger
);
}
if
(
group
.
regions
!=
null
){
}
scriptManager
.
meetEntities
(
entities
);
//scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null);
//groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null));
Grasscutter
.
getLogger
().
info
(
"Scene {} loaded {} group(s)"
,
this
.
getId
(),
groups
.
size
());
}
public
void
onUnloadBlock
(
SceneBlock
block
)
{
List
<
GameEntity
>
toRemove
=
this
.
getEntities
().
values
().
stream
()
.
filter
(
e
->
e
.
getBlockId
()
==
block
.
id
).
toList
();
if
(
toRemove
.
size
()
>
0
)
{
toRemove
.
forEach
(
this
::
removeEntityDirectly
);
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
toRemove
,
VisionType
.
VISION_TYPE_REMOVE
));
}
for
(
SceneGroup
group
:
block
.
groups
.
values
())
{
if
(
group
.
triggers
!=
null
)
{
group
.
triggers
.
values
().
forEach
(
getScriptManager
()::
deregisterTrigger
);
}
if
(
group
.
regions
!=
null
)
{
group
.
regions
.
values
().
forEach
(
getScriptManager
()::
deregisterRegion
);
}
}
scriptManager
.
getLoadedGroupSetPerBlock
().
remove
(
block
.
id
);
Grasscutter
.
getLogger
().
info
(
"Scene {} Block {} is unloaded."
,
this
.
getId
(),
block
.
id
);
}
// Gadgets
public
void
onPlayerCreateGadget
(
EntityClientGadget
gadget
)
{
// Directly add
this
.
addEntityDirectly
(
gadget
);
// Add to owner's gadget list
gadget
.
getOwner
().
getTeamManager
().
getGadgets
().
add
(
gadget
);
// Optimization
if
(
this
.
getPlayerCount
()
==
1
&&
this
.
getPlayers
().
get
(
0
)
==
gadget
.
getOwner
())
{
return
;
}
this
.
broadcastPacketToOthers
(
gadget
.
getOwner
(),
new
PacketSceneEntityAppearNotify
(
gadget
));
}
public
void
onPlayerDestroyGadget
(
int
entityId
)
{
GameEntity
entity
=
getEntities
().
get
(
entityId
);
if
(
entity
==
null
||
!(
entity
instanceof
EntityClientGadget
))
{
return
;
}
// Get and remove entity
EntityClientGadget
gadget
=
(
EntityClientGadget
)
entity
;
this
.
removeEntityDirectly
(
gadget
);
// Remove from owner's gadget list
gadget
.
getOwner
().
getTeamManager
().
getGadgets
().
remove
(
gadget
);
// Optimization
if
(
this
.
getPlayerCount
()
==
1
&&
this
.
getPlayers
().
get
(
0
)
==
gadget
.
getOwner
())
{
return
;
}
this
.
broadcastPacketToOthers
(
gadget
.
getOwner
(),
new
PacketSceneEntityDisappearNotify
(
gadget
,
VisionType
.
VISION_TYPE_DIE
));
}
// Broadcasting
public
void
broadcastPacket
(
BasePacket
packet
)
{
// Send to all players - might have to check if player has been sent data packets
for
(
Player
player
:
this
.
getPlayers
())
{
player
.
getSession
().
send
(
packet
);
}
}
public
void
broadcastPacketToOthers
(
Player
excludedPlayer
,
BasePacket
packet
)
{
// Optimization
if
(
this
.
getPlayerCount
()
==
1
&&
this
.
getPlayers
().
get
(
0
)
==
excludedPlayer
)
{
return
;
}
// Send to all players - might have to check if player has been sent data packets
for
(
Player
player
:
this
.
getPlayers
())
{
if
(
player
==
excludedPlayer
)
{
continue
;
}
// Send
player
.
getSession
().
send
(
packet
);
}
}
public
void
addItemEntity
(
int
itemId
,
int
amount
,
GameEntity
bornForm
){
ItemData
itemData
=
GameData
.
getItemDataMap
().
get
(
itemId
);
if
(
itemData
==
null
)
{
return
;
}
if
(
itemData
.
isEquip
())
{
float
range
=
(
3
f
+
(.
1
f
*
amount
));
for
(
int
i
=
0
;
i
<
amount
;
i
++)
{
Position
pos
=
bornForm
.
getPosition
().
clone
().
addX
((
float
)
(
Math
.
random
()
*
range
)
-
(
range
/
2
)).
addZ
((
float
)
(
Math
.
random
()
*
range
)
-
(
range
/
2
)).
addZ
(.
9
f
);
EntityItem
entity
=
new
EntityItem
(
this
,
null
,
itemData
,
pos
,
1
);
addEntity
(
entity
);
}
}
else
{
EntityItem
entity
=
new
EntityItem
(
this
,
null
,
itemData
,
bornForm
.
getPosition
().
clone
().
addZ
(.
9
f
),
amount
);
addEntity
(
entity
);
}
}
public
void
loadNpcForPlayerEnter
(
Player
player
){
}
}
scriptManager
.
getLoadedGroupSetPerBlock
().
remove
(
block
.
id
);
Grasscutter
.
getLogger
().
info
(
"Scene {} Block {} is unloaded."
,
this
.
getId
(),
block
.
id
);
}
// Gadgets
public
void
onPlayerCreateGadget
(
EntityClientGadget
gadget
)
{
// Directly add
this
.
addEntityDirectly
(
gadget
);
// Add to owner's gadget list
gadget
.
getOwner
().
getTeamManager
().
getGadgets
().
add
(
gadget
);
// Optimization
if
(
this
.
getPlayerCount
()
==
1
&&
this
.
getPlayers
().
get
(
0
)
==
gadget
.
getOwner
())
{
return
;
}
this
.
broadcastPacketToOthers
(
gadget
.
getOwner
(),
new
PacketSceneEntityAppearNotify
(
gadget
));
}
public
void
onPlayerDestroyGadget
(
int
entityId
)
{
GameEntity
entity
=
getEntities
().
get
(
entityId
);
if
(
entity
==
null
||
!(
entity
instanceof
EntityClientGadget
))
{
return
;
}
// Get and remove entity
EntityClientGadget
gadget
=
(
EntityClientGadget
)
entity
;
this
.
removeEntityDirectly
(
gadget
);
// Remove from owner's gadget list
gadget
.
getOwner
().
getTeamManager
().
getGadgets
().
remove
(
gadget
);
// Optimization
if
(
this
.
getPlayerCount
()
==
1
&&
this
.
getPlayers
().
get
(
0
)
==
gadget
.
getOwner
())
{
return
;
}
this
.
broadcastPacketToOthers
(
gadget
.
getOwner
(),
new
PacketSceneEntityDisappearNotify
(
gadget
,
VisionType
.
VISION_TYPE_DIE
));
}
// Broadcasting
public
void
broadcastPacket
(
BasePacket
packet
)
{
// Send to all players - might have to check if player has been sent data packets
for
(
Player
player
:
this
.
getPlayers
())
{
player
.
getSession
().
send
(
packet
);
}
}
public
void
broadcastPacketToOthers
(
Player
excludedPlayer
,
BasePacket
packet
)
{
// Optimization
if
(
this
.
getPlayerCount
()
==
1
&&
this
.
getPlayers
().
get
(
0
)
==
excludedPlayer
)
{
return
;
}
// Send to all players - might have to check if player has been sent data packets
for
(
Player
player
:
this
.
getPlayers
())
{
if
(
player
==
excludedPlayer
)
{
continue
;
}
// Send
player
.
getSession
().
send
(
packet
);
}
}
public
void
addItemEntity
(
int
itemId
,
int
amount
,
GameEntity
bornForm
)
{
ItemData
itemData
=
GameData
.
getItemDataMap
().
get
(
itemId
);
if
(
itemData
==
null
)
{
return
;
}
if
(
itemData
.
isEquip
())
{
float
range
=
(
3
f
+
(.
1
f
*
amount
));
for
(
int
i
=
0
;
i
<
amount
;
i
++)
{
Position
pos
=
bornForm
.
getPosition
().
clone
().
addX
((
float
)
(
Math
.
random
()
*
range
)
-
(
range
/
2
)).
addZ
((
float
)
(
Math
.
random
()
*
range
)
-
(
range
/
2
)).
addZ
(.
9
f
);
EntityItem
entity
=
new
EntityItem
(
this
,
null
,
itemData
,
pos
,
1
);
addEntity
(
entity
);
}
}
else
{
EntityItem
entity
=
new
EntityItem
(
this
,
null
,
itemData
,
bornForm
.
getPosition
().
clone
().
addZ
(.
9
f
),
amount
);
addEntity
(
entity
);
}
}
public
void
loadNpcForPlayerEnter
(
Player
player
)
{
this
.
npcBornEntrySet
.
addAll
(
loadNpcForPlayer
(
player
));
}
private
List
<
SceneNpcBornEntry
>
loadNpcForPlayer
(
Player
player
){
var
pos
=
player
.
getPosition
();
var
data
=
GameData
.
getSceneNpcBornData
().
get
(
getId
());
if
(
data
==
null
){
return
List
.
of
();
}
private
List
<
SceneNpcBornEntry
>
loadNpcForPlayer
(
Player
player
)
{
var
pos
=
player
.
getPosition
();
var
data
=
GameData
.
getSceneNpcBornData
().
get
(
getId
());
if
(
data
==
null
)
{
return
List
.
of
();
}
var
npcList
=
SceneIndexManager
.
queryNeighbors
(
data
.
getIndex
(),
pos
.
toDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
var
npcList
=
SceneIndexManager
.
queryNeighbors
(
data
.
getIndex
(),
pos
.
toDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
var
sceneNpcBornEntries
=
npcList
.
stream
()
var
sceneNpcBornEntries
=
npcList
.
stream
()
.
filter
(
i
->
!
this
.
npcBornEntrySet
.
contains
(
i
))
.
toList
();
if
(
sceneNpcBornEntries
.
size
()
>
0
){
this
.
broadcastPacket
(
new
PacketGroupSuiteNotify
(
sceneNpcBornEntries
));
if
(
sceneNpcBornEntries
.
size
()
>
0
)
{
this
.
broadcastPacket
(
new
PacketGroupSuiteNotify
(
sceneNpcBornEntries
));
Grasscutter
.
getLogger
().
debug
(
"Loaded Npc Group Suite {}"
,
sceneNpcBornEntries
);
}
}
return
npcList
;
}
}
public
void
loadGroupForQuest
(
List
<
QuestGroupSuite
>
sceneGroupSuite
)
{
if
(!
scriptManager
.
isInit
()){
if
(!
scriptManager
.
isInit
())
{
return
;
}
sceneGroupSuite
.
forEach
(
i
->
{
var
group
=
scriptManager
.
getGroupById
(
i
.
getGroup
());
if
(
group
==
null
){
if
(
group
==
null
)
{
return
;
}
var
suite
=
group
.
getSuiteByIndex
(
i
.
getSuite
());
if
(
suite
==
null
){
if
(
suite
==
null
)
{
return
;
}
scriptManager
.
addGroupSuite
(
group
,
suite
);
...
...
src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java
View file @
ae2d1fe4
...
...
@@ -7,62 +7,62 @@ import emu.grasscutter.data.GameDepot;
import
emu.grasscutter.utils.Position
;
public
class
SpawnDataEntry
{
private
transient
SpawnGroupEntry
group
;
private
int
monsterId
;
private
int
gadgetId
;
private
int
configId
;
private
int
level
;
private
int
poseId
;
private
int
gatherItemId
;
private
transient
SpawnGroupEntry
group
;
private
int
monsterId
;
private
int
gadgetId
;
private
int
configId
;
private
int
level
;
private
int
poseId
;
private
int
gatherItemId
;
private
int
gadgetState
;
private
Position
pos
;
private
Position
rot
;
private
Position
pos
;
private
Position
rot
;
public
SpawnGroupEntry
getGroup
()
{
return
group
;
}
public
SpawnGroupEntry
getGroup
()
{
return
group
;
}
public
void
setGroup
(
SpawnGroupEntry
group
)
{
this
.
group
=
group
;
}
public
void
setGroup
(
SpawnGroupEntry
group
)
{
this
.
group
=
group
;
}
public
int
getMonsterId
()
{
return
monsterId
;
}
public
int
getMonsterId
()
{
return
monsterId
;
}
public
int
getGadgetId
()
{
return
gadgetId
;
}
public
int
getGadgetId
()
{
return
gadgetId
;
}
public
int
getGadgetState
()
{
return
gadgetState
;
}
public
int
getConfigId
()
{
return
configId
;
}
return
configId
;
}
public
int
getLevel
()
{
return
level
;
}
public
int
getLevel
()
{
return
level
;
}
public
int
getPoseId
()
{
return
poseId
;
}
public
int
getPoseId
()
{
return
poseId
;
}
public
int
getGatherItemId
()
{
return
gatherItemId
;
}
public
int
getGatherItemId
()
{
return
gatherItemId
;
}
public
Position
getPos
()
{
return
pos
;
}
public
Position
getPos
()
{
return
pos
;
}
public
Position
getRot
()
{
return
rot
;
}
public
Position
getRot
()
{
return
rot
;
}
public
GridBlockId
getBlockId
(){
public
GridBlockId
getBlockId
()
{
int
scale
=
GridBlockId
.
getScale
(
gadgetId
);
return
new
GridBlockId
(
group
.
sceneId
,
scale
,
(
int
)(
pos
.
getX
()
/
GameDepot
.
BLOCK_SIZE
[
scale
]),
...
...
@@ -70,32 +70,32 @@ public class SpawnDataEntry {
);
}
public
static
class
SpawnGroupEntry
{
private
int
sceneId
;
private
int
groupId
;
private
int
blockId
;
private
List
<
SpawnDataEntry
>
spawns
;
public
static
class
SpawnGroupEntry
{
private
int
sceneId
;
private
int
groupId
;
private
int
blockId
;
private
List
<
SpawnDataEntry
>
spawns
;
public
int
getSceneId
()
{
return
sceneId
;
}
public
int
getSceneId
()
{
return
sceneId
;
}
public
int
getGroupId
()
{
return
groupId
;
}
public
int
getGroupId
()
{
return
groupId
;
}
public
int
getBlockId
()
{
return
blockId
;
}
public
int
getBlockId
()
{
return
blockId
;
}
public
void
setBlockId
(
int
blockId
)
{
this
.
blockId
=
blockId
;
}
public
void
setBlockId
(
int
blockId
)
{
this
.
blockId
=
blockId
;
}
public
List
<
SpawnDataEntry
>
getSpawns
()
{
return
spawns
;
}
}
public
List
<
SpawnDataEntry
>
getSpawns
()
{
return
spawns
;
}
}
public
static
class
GridBlockId
{
int
sceneId
;
...
...
@@ -133,7 +133,7 @@ public class SpawnDataEntry {
return
Objects
.
hash
(
sceneId
,
scale
,
x
,
z
);
}
public
static
GridBlockId
[]
getAdjacentGridBlockIds
(
int
sceneId
,
Position
pos
){
public
static
GridBlockId
[]
getAdjacentGridBlockIds
(
int
sceneId
,
Position
pos
)
{
GridBlockId
[]
results
=
new
GridBlockId
[
5
*
5
*
GameDepot
.
BLOCK_SIZE
.
length
];
int
t
=
0
;
for
(
int
scale
=
0
;
scale
<
GameDepot
.
BLOCK_SIZE
.
length
;
scale
++)
{
...
...
@@ -148,7 +148,7 @@ public class SpawnDataEntry {
return
results
;
}
public
static
int
getScale
(
int
gadgetId
){
public
static
int
getScale
(
int
gadgetId
)
{
return
0
;
//you should implement here,this is index of GameDepot.BLOCK_SIZE
}
}
...
...
src/main/java/emu/grasscutter/game/world/World.java
View file @
ae2d1fe4
...
...
@@ -31,298 +31,298 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
public
class
World
implements
Iterable
<
Player
>
{
private
final
GameServer
server
;
private
final
Player
owner
;
private
final
List
<
Player
>
players
;
private
final
Int2ObjectMap
<
Scene
>
scenes
;
private
int
levelEntityId
;
private
int
nextEntityId
=
0
;
private
int
nextPeerId
=
0
;
private
int
worldLevel
;
private
boolean
isMultiplayer
;
public
World
(
Player
player
)
{
this
(
player
,
false
);
}
public
World
(
Player
player
,
boolean
isMultiplayer
)
{
this
.
owner
=
player
;
this
.
server
=
player
.
getServer
();
this
.
players
=
Collections
.
synchronizedList
(
new
ArrayList
<>());
this
.
scenes
=
Int2ObjectMaps
.
synchronize
(
new
Int2ObjectOpenHashMap
<>());
this
.
levelEntityId
=
getNextEntityId
(
EntityIdType
.
MPLEVEL
);
this
.
worldLevel
=
player
.
getWorldLevel
();
this
.
isMultiplayer
=
isMultiplayer
;
this
.
owner
.
getServer
().
registerWorld
(
this
);
}
public
Player
getHost
()
{
return
owner
;
}
public
GameServer
getServer
()
{
return
server
;
}
public
int
getLevelEntityId
()
{
return
levelEntityId
;
}
public
int
getHostPeerId
()
{
if
(
this
.
getHost
()
==
null
)
{
return
0
;
}
return
this
.
getHost
().
getPeerId
();
}
public
int
getNextPeerId
()
{
return
++
this
.
nextPeerId
;
}
public
int
getWorldLevel
()
{
return
worldLevel
;
}
public
void
setWorldLevel
(
int
worldLevel
)
{
this
.
worldLevel
=
worldLevel
;
}
public
List
<
Player
>
getPlayers
()
{
return
players
;
}
public
Int2ObjectMap
<
Scene
>
getScenes
()
{
return
this
.
scenes
;
}
public
Scene
getSceneById
(
int
sceneId
)
{
// Get scene normally
Scene
scene
=
getScenes
().
get
(
sceneId
);
if
(
scene
!=
null
)
{
return
scene
;
}
// Create scene from scene data if it doesnt exist
SceneData
sceneData
=
GameData
.
getSceneDataMap
().
get
(
sceneId
);
if
(
sceneData
!=
null
)
{
scene
=
new
Scene
(
this
,
sceneData
);
this
.
registerScene
(
scene
);
return
scene
;
}
return
null
;
}
public
int
getPlayerCount
()
{
return
getPlayers
().
size
();
}
public
boolean
isMultiplayer
()
{
return
isMultiplayer
;
}
public
int
getNextEntityId
(
EntityIdType
idType
)
{
return
(
idType
.
getId
()
<<
24
)
+
++
this
.
nextEntityId
;
}
public
synchronized
void
addPlayer
(
Player
player
)
{
// Check if player already in
if
(
getPlayers
().
contains
(
player
))
{
return
;
}
// Remove player from prev world
if
(
player
.
getWorld
()
!=
null
)
{
player
.
getWorld
().
removePlayer
(
player
);
}
// Register
player
.
setWorld
(
this
);
getPlayers
().
add
(
player
);
// Set player variables
player
.
setPeerId
(
this
.
getNextPeerId
());
player
.
getTeamManager
().
setEntityId
(
getNextEntityId
(
EntityIdType
.
TEAM
));
// Copy main team to mp team
if
(
this
.
isMultiplayer
())
{
player
.
getTeamManager
().
getMpTeam
().
copyFrom
(
player
.
getTeamManager
().
getCurrentSinglePlayerTeamInfo
(),
player
.
getTeamManager
().
getMaxTeamSize
());
player
.
getTeamManager
().
setCurrentCharacterIndex
(
0
);
}
// Add to scene
Scene
scene
=
this
.
getSceneById
(
player
.
getSceneId
());
scene
.
addPlayer
(
player
);
// Info packet for other players
if
(
this
.
getPlayers
().
size
()
>
1
)
{
this
.
updatePlayerInfos
(
player
);
}
}
public
synchronized
void
removePlayer
(
Player
player
)
{
// Remove team entities
player
.
sendPacket
(
new
PacketDelTeamEntityNotify
(
player
.
getSceneId
(),
getPlayers
().
stream
().
map
(
p
->
p
.
getTeamManager
().
getEntityId
()).
collect
(
Collectors
.
toList
())
)
);
// Deregister
getPlayers
().
remove
(
player
);
player
.
setWorld
(
null
);
// Remove from scene
Scene
scene
=
this
.
getSceneById
(
player
.
getSceneId
());
scene
.
removePlayer
(
player
);
// Info packet for other players
if
(
this
.
getPlayers
().
size
()
>
0
)
{
this
.
updatePlayerInfos
(
player
);
}
// Disband world if host leaves
if
(
getHost
()
==
player
)
{
List
<
Player
>
kicked
=
new
ArrayList
<>(
this
.
getPlayers
());
for
(
Player
victim
:
kicked
)
{
World
world
=
new
World
(
victim
);
world
.
addPlayer
(
victim
);
victim
.
sendPacket
(
new
PacketPlayerEnterSceneNotify
(
victim
,
EnterType
.
ENTER_TYPE_SELF
,
EnterReason
.
TeamKick
,
victim
.
getSceneId
(),
victim
.
getPosition
()));
}
}
}
public
void
registerScene
(
Scene
scene
)
{
this
.
getScenes
().
put
(
scene
.
getId
(),
scene
);
}
public
void
deregisterScene
(
Scene
scene
)
{
this
.
getScenes
().
remove
(
scene
.
getId
());
}
public
boolean
transferPlayerToScene
(
Player
player
,
int
sceneId
,
Position
pos
)
{
return
transferPlayerToScene
(
player
,
sceneId
,
null
,
pos
);
}
public
boolean
transferPlayerToScene
(
Player
player
,
int
sceneId
,
DungeonData
data
)
{
return
transferPlayerToScene
(
player
,
sceneId
,
data
,
null
);
}
public
boolean
transferPlayerToScene
(
Player
player
,
int
sceneId
,
DungeonData
dungeonData
,
Position
pos
)
{
if
(
GameData
.
getSceneDataMap
().
get
(
sceneId
)
==
null
)
{
return
false
;
}
Scene
oldScene
=
null
;
if
(
player
.
getScene
()
!=
null
)
{
oldScene
=
player
.
getScene
();
// Dont deregister scenes if the player is going to tp back into them
if
(
oldScene
.
getId
()
==
sceneId
)
{
oldScene
.
setDontDestroyWhenEmpty
(
true
);
}
oldScene
.
removePlayer
(
player
);
}
Scene
newScene
=
this
.
getSceneById
(
sceneId
);
newScene
.
setDungeonData
(
dungeonData
);
newScene
.
addPlayer
(
player
);
// Dungeon
SceneConfig
config
=
newScene
.
getScriptManager
().
getConfig
();
if
(
pos
==
null
&&
config
!=
null
)
{
if
(
config
.
born_pos
!=
null
)
{
pos
=
newScene
.
getScriptManager
().
getConfig
().
born_pos
;
}
if
(
config
.
born_rot
!=
null
)
{
player
.
getRotation
().
set
(
config
.
born_rot
);
}
}
// Set player position
if
(
pos
==
null
)
{
pos
=
player
.
getPosition
();
}
player
.
getPosition
().
set
(
pos
);
if
(
oldScene
!=
null
)
{
newScene
.
setPrevScene
(
oldScene
.
getId
());
oldScene
.
setDontDestroyWhenEmpty
(
false
);
}
// Get enter types
EnterType
enterType
=
EnterType
.
ENTER_TYPE_JUMP
;
EnterReason
enterReason
=
EnterReason
.
TransPoint
;
if
(
dungeonData
!=
null
)
{
enterType
=
EnterType
.
ENTER_TYPE_DUNGEON
;
enterReason
=
EnterReason
.
DungeonEnter
;
}
else
if
(
oldScene
==
newScene
)
{
enterType
=
EnterType
.
ENTER_TYPE_GOTO
;
}
else
if
(
newScene
.
getSceneType
()
==
SceneType
.
SCENE_HOME_WORLD
)
{
// Home
enterReason
=
EnterReason
.
EnterHome
;
enterType
=
EnterType
.
ENTER_TYPE_SELF_HOME
;
}
// Teleport packet
player
.
sendPacket
(
new
PacketPlayerEnterSceneNotify
(
player
,
enterType
,
enterReason
,
sceneId
,
pos
));
return
true
;
}
private
void
updatePlayerInfos
(
Player
paramPlayer
)
{
for
(
Player
player
:
getPlayers
())
{
// Dont send packets if player is loading in and filter out joining player
if
(!
player
.
hasSentAvatarDataNotify
()
||
player
.
getSceneLoadState
().
getValue
()
<
SceneLoadState
.
INIT
.
getValue
()
||
player
==
paramPlayer
)
{
continue
;
}
// Update team of all players since max players has been changed - Probably not the best way to do it
if
(
this
.
isMultiplayer
())
{
player
.
getTeamManager
().
getMpTeam
().
copyFrom
(
player
.
getTeamManager
().
getMpTeam
(),
player
.
getTeamManager
().
getMaxTeamSize
());
player
.
getTeamManager
().
updateTeamEntities
(
null
);
}
// World player info packets
player
.
getSession
().
send
(
new
PacketWorldPlayerInfoNotify
(
this
));
player
.
getSession
().
send
(
new
PacketScenePlayerInfoNotify
(
this
));
player
.
getSession
().
send
(
new
PacketWorldPlayerRTTNotify
(
this
));
// Team packets
player
.
getSession
().
send
(
new
PacketSyncTeamEntityNotify
(
player
));
player
.
getSession
().
send
(
new
PacketSyncScenePlayTeamEntityNotify
(
player
));
}
}
public
void
broadcastPacket
(
BasePacket
packet
)
{
// Send to all players - might have to check if player has been sent data packets
for
(
Player
player
:
this
.
getPlayers
())
{
player
.
getSession
().
send
(
packet
);
}
}
public
void
onTick
()
{
for
(
Scene
scene
:
this
.
getScenes
().
values
())
{
scene
.
onTick
();
}
}
public
void
close
()
{
}
@Override
public
Iterator
<
Player
>
iterator
()
{
return
getPlayers
().
iterator
();
}
private
final
GameServer
server
;
private
final
Player
owner
;
private
final
List
<
Player
>
players
;
private
final
Int2ObjectMap
<
Scene
>
scenes
;
private
int
levelEntityId
;
private
int
nextEntityId
=
0
;
private
int
nextPeerId
=
0
;
private
int
worldLevel
;
private
boolean
isMultiplayer
;
public
World
(
Player
player
)
{
this
(
player
,
false
);
}
public
World
(
Player
player
,
boolean
isMultiplayer
)
{
this
.
owner
=
player
;
this
.
server
=
player
.
getServer
();
this
.
players
=
Collections
.
synchronizedList
(
new
ArrayList
<>());
this
.
scenes
=
Int2ObjectMaps
.
synchronize
(
new
Int2ObjectOpenHashMap
<>());
this
.
levelEntityId
=
getNextEntityId
(
EntityIdType
.
MPLEVEL
);
this
.
worldLevel
=
player
.
getWorldLevel
();
this
.
isMultiplayer
=
isMultiplayer
;
this
.
owner
.
getServer
().
registerWorld
(
this
);
}
public
Player
getHost
()
{
return
owner
;
}
public
GameServer
getServer
()
{
return
server
;
}
public
int
getLevelEntityId
()
{
return
levelEntityId
;
}
public
int
getHostPeerId
()
{
if
(
this
.
getHost
()
==
null
)
{
return
0
;
}
return
this
.
getHost
().
getPeerId
();
}
public
int
getNextPeerId
()
{
return
++
this
.
nextPeerId
;
}
public
int
getWorldLevel
()
{
return
worldLevel
;
}
public
void
setWorldLevel
(
int
worldLevel
)
{
this
.
worldLevel
=
worldLevel
;
}
public
List
<
Player
>
getPlayers
()
{
return
players
;
}
public
Int2ObjectMap
<
Scene
>
getScenes
()
{
return
this
.
scenes
;
}
public
Scene
getSceneById
(
int
sceneId
)
{
// Get scene normally
Scene
scene
=
getScenes
().
get
(
sceneId
);
if
(
scene
!=
null
)
{
return
scene
;
}
// Create scene from scene data if it doesnt exist
SceneData
sceneData
=
GameData
.
getSceneDataMap
().
get
(
sceneId
);
if
(
sceneData
!=
null
)
{
scene
=
new
Scene
(
this
,
sceneData
);
this
.
registerScene
(
scene
);
return
scene
;
}
return
null
;
}
public
int
getPlayerCount
()
{
return
getPlayers
().
size
();
}
public
boolean
isMultiplayer
()
{
return
isMultiplayer
;
}
public
int
getNextEntityId
(
EntityIdType
idType
)
{
return
(
idType
.
getId
()
<<
24
)
+
++
this
.
nextEntityId
;
}
public
synchronized
void
addPlayer
(
Player
player
)
{
// Check if player already in
if
(
getPlayers
().
contains
(
player
))
{
return
;
}
// Remove player from prev world
if
(
player
.
getWorld
()
!=
null
)
{
player
.
getWorld
().
removePlayer
(
player
);
}
// Register
player
.
setWorld
(
this
);
getPlayers
().
add
(
player
);
// Set player variables
player
.
setPeerId
(
this
.
getNextPeerId
());
player
.
getTeamManager
().
setEntityId
(
getNextEntityId
(
EntityIdType
.
TEAM
));
// Copy main team to mp team
if
(
this
.
isMultiplayer
())
{
player
.
getTeamManager
().
getMpTeam
().
copyFrom
(
player
.
getTeamManager
().
getCurrentSinglePlayerTeamInfo
(),
player
.
getTeamManager
().
getMaxTeamSize
());
player
.
getTeamManager
().
setCurrentCharacterIndex
(
0
);
}
// Add to scene
Scene
scene
=
this
.
getSceneById
(
player
.
getSceneId
());
scene
.
addPlayer
(
player
);
// Info packet for other players
if
(
this
.
getPlayers
().
size
()
>
1
)
{
this
.
updatePlayerInfos
(
player
);
}
}
public
synchronized
void
removePlayer
(
Player
player
)
{
// Remove team entities
player
.
sendPacket
(
new
PacketDelTeamEntityNotify
(
player
.
getSceneId
(),
getPlayers
().
stream
().
map
(
p
->
p
.
getTeamManager
().
getEntityId
()).
collect
(
Collectors
.
toList
())
)
);
// Deregister
getPlayers
().
remove
(
player
);
player
.
setWorld
(
null
);
// Remove from scene
Scene
scene
=
this
.
getSceneById
(
player
.
getSceneId
());
scene
.
removePlayer
(
player
);
// Info packet for other players
if
(
this
.
getPlayers
().
size
()
>
0
)
{
this
.
updatePlayerInfos
(
player
);
}
// Disband world if host leaves
if
(
getHost
()
==
player
)
{
List
<
Player
>
kicked
=
new
ArrayList
<>(
this
.
getPlayers
());
for
(
Player
victim
:
kicked
)
{
World
world
=
new
World
(
victim
);
world
.
addPlayer
(
victim
);
victim
.
sendPacket
(
new
PacketPlayerEnterSceneNotify
(
victim
,
EnterType
.
ENTER_TYPE_SELF
,
EnterReason
.
TeamKick
,
victim
.
getSceneId
(),
victim
.
getPosition
()));
}
}
}
public
void
registerScene
(
Scene
scene
)
{
this
.
getScenes
().
put
(
scene
.
getId
(),
scene
);
}
public
void
deregisterScene
(
Scene
scene
)
{
this
.
getScenes
().
remove
(
scene
.
getId
());
}
public
boolean
transferPlayerToScene
(
Player
player
,
int
sceneId
,
Position
pos
)
{
return
transferPlayerToScene
(
player
,
sceneId
,
null
,
pos
);
}
public
boolean
transferPlayerToScene
(
Player
player
,
int
sceneId
,
DungeonData
data
)
{
return
transferPlayerToScene
(
player
,
sceneId
,
data
,
null
);
}
public
boolean
transferPlayerToScene
(
Player
player
,
int
sceneId
,
DungeonData
dungeonData
,
Position
pos
)
{
if
(
GameData
.
getSceneDataMap
().
get
(
sceneId
)
==
null
)
{
return
false
;
}
Scene
oldScene
=
null
;
if
(
player
.
getScene
()
!=
null
)
{
oldScene
=
player
.
getScene
();
// Dont deregister scenes if the player is going to tp back into them
if
(
oldScene
.
getId
()
==
sceneId
)
{
oldScene
.
setDontDestroyWhenEmpty
(
true
);
}
oldScene
.
removePlayer
(
player
);
}
Scene
newScene
=
this
.
getSceneById
(
sceneId
);
newScene
.
setDungeonData
(
dungeonData
);
newScene
.
addPlayer
(
player
);
// Dungeon
SceneConfig
config
=
newScene
.
getScriptManager
().
getConfig
();
if
(
pos
==
null
&&
config
!=
null
)
{
if
(
config
.
born_pos
!=
null
)
{
pos
=
newScene
.
getScriptManager
().
getConfig
().
born_pos
;
}
if
(
config
.
born_rot
!=
null
)
{
player
.
getRotation
().
set
(
config
.
born_rot
);
}
}
// Set player position
if
(
pos
==
null
)
{
pos
=
player
.
getPosition
();
}
player
.
getPosition
().
set
(
pos
);
if
(
oldScene
!=
null
)
{
newScene
.
setPrevScene
(
oldScene
.
getId
());
oldScene
.
setDontDestroyWhenEmpty
(
false
);
}
// Get enter types
EnterType
enterType
=
EnterType
.
ENTER_TYPE_JUMP
;
EnterReason
enterReason
=
EnterReason
.
TransPoint
;
if
(
dungeonData
!=
null
)
{
enterType
=
EnterType
.
ENTER_TYPE_DUNGEON
;
enterReason
=
EnterReason
.
DungeonEnter
;
}
else
if
(
oldScene
==
newScene
)
{
enterType
=
EnterType
.
ENTER_TYPE_GOTO
;
}
else
if
(
newScene
.
getSceneType
()
==
SceneType
.
SCENE_HOME_WORLD
)
{
// Home
enterReason
=
EnterReason
.
EnterHome
;
enterType
=
EnterType
.
ENTER_TYPE_SELF_HOME
;
}
// Teleport packet
player
.
sendPacket
(
new
PacketPlayerEnterSceneNotify
(
player
,
enterType
,
enterReason
,
sceneId
,
pos
));
return
true
;
}
private
void
updatePlayerInfos
(
Player
paramPlayer
)
{
for
(
Player
player
:
getPlayers
())
{
// Dont send packets if player is loading in and filter out joining player
if
(!
player
.
hasSentAvatarDataNotify
()
||
player
.
getSceneLoadState
().
getValue
()
<
SceneLoadState
.
INIT
.
getValue
()
||
player
==
paramPlayer
)
{
continue
;
}
// Update team of all players since max players has been changed - Probably not the best way to do it
if
(
this
.
isMultiplayer
())
{
player
.
getTeamManager
().
getMpTeam
().
copyFrom
(
player
.
getTeamManager
().
getMpTeam
(),
player
.
getTeamManager
().
getMaxTeamSize
());
player
.
getTeamManager
().
updateTeamEntities
(
null
);
}
// World player info packets
player
.
getSession
().
send
(
new
PacketWorldPlayerInfoNotify
(
this
));
player
.
getSession
().
send
(
new
PacketScenePlayerInfoNotify
(
this
));
player
.
getSession
().
send
(
new
PacketWorldPlayerRTTNotify
(
this
));
// Team packets
player
.
getSession
().
send
(
new
PacketSyncTeamEntityNotify
(
player
));
player
.
getSession
().
send
(
new
PacketSyncScenePlayTeamEntityNotify
(
player
));
}
}
public
void
broadcastPacket
(
BasePacket
packet
)
{
// Send to all players - might have to check if player has been sent data packets
for
(
Player
player
:
this
.
getPlayers
())
{
player
.
getSession
().
send
(
packet
);
}
}
public
void
onTick
()
{
for
(
Scene
scene
:
this
.
getScenes
().
values
())
{
scene
.
onTick
();
}
}
public
void
close
()
{
}
@Override
public
Iterator
<
Player
>
iterator
()
{
return
getPlayers
().
iterator
();
}
}
src/main/java/emu/grasscutter/game/world/WorldDataSystem.java
View file @
ae2d1fe4
...
...
@@ -30,7 +30,7 @@ public class WorldDataSystem extends BaseGameSystem {
private
final
Map
<
String
,
ChestInteractHandler
>
chestInteractHandlerMap
;
// chestType-Handler
private
final
Map
<
String
,
SceneGroup
>
sceneInvestigationGroupMap
;
// <sceneId_groupId, Group>
public
WorldDataSystem
(
GameServer
server
){
public
WorldDataSystem
(
GameServer
server
)
{
super
(
server
);
this
.
chestInteractHandlerMap
=
new
HashMap
<>();
this
.
sceneInvestigationGroupMap
=
new
ConcurrentHashMap
<>();
...
...
@@ -38,15 +38,15 @@ public class WorldDataSystem extends BaseGameSystem {
loadChestConfig
();
}
public
synchronized
void
loadChestConfig
(){
public
synchronized
void
loadChestConfig
()
{
// set the special chest first
chestInteractHandlerMap
.
put
(
"SceneObj_Chest_Flora"
,
new
BossChestInteractHandler
());
try
(
Reader
reader
=
DataLoader
.
loadReader
(
"ChestReward.json"
))
{
try
(
Reader
reader
=
DataLoader
.
loadReader
(
"ChestReward.json"
))
{
List
<
ChestReward
>
chestReward
=
Grasscutter
.
getGsonFactory
().
fromJson
(
reader
,
reader
,
TypeToken
.
getParameterized
(
List
.
class
,
ChestReward
.
class
).
getType
());
chestReward
.
forEach
(
reward
->
reward
.
getObjNames
().
forEach
(
name
->
chestInteractHandlerMap
.
putIfAbsent
(
name
,
new
NormalChestInteractHandler
(
reward
))));
...
...
@@ -60,21 +60,21 @@ public class WorldDataSystem extends BaseGameSystem {
return
chestInteractHandlerMap
;
}
public
RewardPreviewData
getRewardByBossId
(
int
monsterId
){
public
RewardPreviewData
getRewardByBossId
(
int
monsterId
)
{
var
investigationMonsterData
=
GameData
.
getInvestigationMonsterDataMap
().
values
().
parallelStream
()
.
filter
(
imd
->
imd
.
getMonsterIdList
()
!=
null
&&
!
imd
.
getMonsterIdList
().
isEmpty
())
.
filter
(
imd
->
imd
.
getMonsterIdList
().
get
(
0
)
==
monsterId
)
.
findFirst
();
if
(
investigationMonsterData
.
isEmpty
()){
if
(
investigationMonsterData
.
isEmpty
())
{
return
null
;
}
return
GameData
.
getRewardPreviewDataMap
().
get
(
investigationMonsterData
.
get
().
getRewardPreviewId
());
}
private
SceneGroup
getInvestigationGroup
(
int
sceneId
,
int
groupId
){
private
SceneGroup
getInvestigationGroup
(
int
sceneId
,
int
groupId
)
{
var
key
=
sceneId
+
"_"
+
groupId
;
if
(!
sceneInvestigationGroupMap
.
containsKey
(
key
)){
if
(!
sceneInvestigationGroupMap
.
containsKey
(
key
))
{
var
group
=
SceneGroup
.
of
(
groupId
).
load
(
sceneId
);
sceneInvestigationGroupMap
.
putIfAbsent
(
key
,
group
);
return
group
;
...
...
@@ -82,7 +82,7 @@ public class WorldDataSystem extends BaseGameSystem {
return
sceneInvestigationGroupMap
.
get
(
key
);
}
public
int
getMonsterLevel
(
SceneMonster
monster
,
World
world
){
public
int
getMonsterLevel
(
SceneMonster
monster
,
World
world
)
{
// Calculate level
int
level
=
monster
.
level
;
WorldLevelData
worldLevelData
=
GameData
.
getWorldLevelDataMap
().
get
(
world
.
getWorldLevel
());
...
...
@@ -98,14 +98,14 @@ public class WorldDataSystem extends BaseGameSystem {
var
sceneId
=
imd
.
getCityData
().
getSceneId
();
var
group
=
getInvestigationGroup
(
sceneId
,
groupId
);
if
(
group
==
null
||
group
.
monsters
==
null
){
if
(
group
==
null
||
group
.
monsters
==
null
)
{
return
null
;
}
var
monster
=
group
.
monsters
.
values
().
stream
()
.
filter
(
x
->
x
.
monster_id
==
monsterId
)
.
findFirst
();
if
(
monster
.
isEmpty
()){
if
(
monster
.
isEmpty
())
{
return
null
;
}
...
...
@@ -122,9 +122,9 @@ public class WorldDataSystem extends BaseGameSystem {
.
setRefreshInterval
(
Integer
.
MAX_VALUE
)
.
setPos
(
monster
.
get
().
pos
.
toProto
());
if
(
"Boss"
.
equals
(
imd
.
getMonsterCategory
())){
if
(
"Boss"
.
equals
(
imd
.
getMonsterCategory
()))
{
var
bossChest
=
group
.
searchBossChestInGroup
();
if
(
bossChest
.
isPresent
()){
if
(
bossChest
.
isPresent
())
{
builder
.
setResin
(
bossChest
.
get
().
resin
);
builder
.
setBossChestNum
(
bossChest
.
get
().
take_num
);
}
...
...
@@ -132,9 +132,9 @@ public class WorldDataSystem extends BaseGameSystem {
return
builder
.
build
();
}
public
List
<
InvestigationMonsterOuterClass
.
InvestigationMonster
>
getInvestigationMonstersByCityId
(
Player
player
,
int
cityId
){
public
List
<
InvestigationMonsterOuterClass
.
InvestigationMonster
>
getInvestigationMonstersByCityId
(
Player
player
,
int
cityId
)
{
var
cityData
=
GameData
.
getCityDataMap
().
get
(
cityId
);
if
(
cityData
==
null
){
if
(
cityData
==
null
)
{
Grasscutter
.
getLogger
().
warn
(
"City not exist {}"
,
cityId
);
return
List
.
of
();
}
...
...
src/main/java/emu/grasscutter/net/packet/BasePacket.java
View file @
ae2d1fe4
...
...
@@ -8,134 +8,134 @@ import emu.grasscutter.net.proto.PacketHeadOuterClass.PacketHead;
import
emu.grasscutter.utils.Crypto
;
public
class
BasePacket
{
private
static
final
int
const1
=
17767
;
// 0x4567
private
static
final
int
const2
=
-
30293
;
// 0x89ab
private
int
opcode
;
private
boolean
shouldBuildHeader
=
false
;
private
byte
[]
header
;
private
byte
[]
data
;
// Encryption
private
boolean
useDispatchKey
;
public
boolean
shouldEncrypt
=
true
;
public
BasePacket
(
int
opcode
)
{
this
.
opcode
=
opcode
;
}
public
BasePacket
(
int
opcode
,
int
clientSequence
)
{
this
.
opcode
=
opcode
;
this
.
buildHeader
(
clientSequence
);
}
public
BasePacket
(
int
opcode
,
boolean
buildHeader
)
{
this
.
opcode
=
opcode
;
this
.
shouldBuildHeader
=
buildHeader
;
}
public
int
getOpcode
()
{
return
opcode
;
}
public
void
setOpcode
(
int
opcode
)
{
this
.
opcode
=
opcode
;
}
public
boolean
useDispatchKey
()
{
return
useDispatchKey
;
}
public
void
setUseDispatchKey
(
boolean
useDispatchKey
)
{
this
.
useDispatchKey
=
useDispatchKey
;
}
public
byte
[]
getHeader
()
{
return
header
;
}
public
void
setHeader
(
byte
[]
header
)
{
this
.
header
=
header
;
}
public
boolean
shouldBuildHeader
()
{
return
shouldBuildHeader
;
}
public
byte
[]
getData
()
{
return
data
;
}
public
void
setData
(
byte
[]
data
)
{
this
.
data
=
data
;
}
public
void
setData
(
GeneratedMessageV3
proto
)
{
this
.
data
=
proto
.
toByteArray
();
}
@SuppressWarnings
(
"rawtypes"
)
public
void
setData
(
GeneratedMessageV3
.
Builder
proto
)
{
this
.
data
=
proto
.
build
().
toByteArray
();
}
public
BasePacket
buildHeader
(
int
clientSequence
)
{
if
(
this
.
getHeader
()
!=
null
&&
clientSequence
==
0
)
{
return
this
;
}
setHeader
(
PacketHead
.
newBuilder
().
setClientSequenceId
(
clientSequence
).
setSentMs
(
System
.
currentTimeMillis
()).
build
().
toByteArray
());
return
this
;
}
public
byte
[]
build
()
{
if
(
getHeader
()
==
null
)
{
this
.
header
=
new
byte
[
0
];
}
if
(
getData
()
==
null
)
{
this
.
data
=
new
byte
[
0
];
}
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
(
2
+
2
+
2
+
4
+
getHeader
().
length
+
getData
().
length
+
2
);
this
.
writeUint16
(
baos
,
const1
);
this
.
writeUint16
(
baos
,
opcode
);
this
.
writeUint16
(
baos
,
header
.
length
);
this
.
writeUint32
(
baos
,
data
.
length
);
this
.
writeBytes
(
baos
,
header
);
this
.
writeBytes
(
baos
,
data
);
this
.
writeUint16
(
baos
,
const2
);
byte
[]
packet
=
baos
.
toByteArray
();
if
(
this
.
shouldEncrypt
)
{
Crypto
.
xor
(
packet
,
this
.
useDispatchKey
()
?
Crypto
.
DISPATCH_KEY
:
Crypto
.
ENCRYPT_KEY
);
}
return
packet
;
}
public
void
writeUint16
(
ByteArrayOutputStream
baos
,
int
i
)
{
// Unsigned short
private
static
final
int
const1
=
17767
;
// 0x4567
private
static
final
int
const2
=
-
30293
;
// 0x89ab
private
int
opcode
;
private
boolean
shouldBuildHeader
=
false
;
private
byte
[]
header
;
private
byte
[]
data
;
// Encryption
private
boolean
useDispatchKey
;
public
boolean
shouldEncrypt
=
true
;
public
BasePacket
(
int
opcode
)
{
this
.
opcode
=
opcode
;
}
public
BasePacket
(
int
opcode
,
int
clientSequence
)
{
this
.
opcode
=
opcode
;
this
.
buildHeader
(
clientSequence
);
}
public
BasePacket
(
int
opcode
,
boolean
buildHeader
)
{
this
.
opcode
=
opcode
;
this
.
shouldBuildHeader
=
buildHeader
;
}
public
int
getOpcode
()
{
return
opcode
;
}
public
void
setOpcode
(
int
opcode
)
{
this
.
opcode
=
opcode
;
}
public
boolean
useDispatchKey
()
{
return
useDispatchKey
;
}
public
void
setUseDispatchKey
(
boolean
useDispatchKey
)
{
this
.
useDispatchKey
=
useDispatchKey
;
}
public
byte
[]
getHeader
()
{
return
header
;
}
public
void
setHeader
(
byte
[]
header
)
{
this
.
header
=
header
;
}
public
boolean
shouldBuildHeader
()
{
return
shouldBuildHeader
;
}
public
byte
[]
getData
()
{
return
data
;
}
public
void
setData
(
byte
[]
data
)
{
this
.
data
=
data
;
}
public
void
setData
(
GeneratedMessageV3
proto
)
{
this
.
data
=
proto
.
toByteArray
();
}
@SuppressWarnings
(
"rawtypes"
)
public
void
setData
(
GeneratedMessageV3
.
Builder
proto
)
{
this
.
data
=
proto
.
build
().
toByteArray
();
}
public
BasePacket
buildHeader
(
int
clientSequence
)
{
if
(
this
.
getHeader
()
!=
null
&&
clientSequence
==
0
)
{
return
this
;
}
setHeader
(
PacketHead
.
newBuilder
().
setClientSequenceId
(
clientSequence
).
setSentMs
(
System
.
currentTimeMillis
()).
build
().
toByteArray
());
return
this
;
}
public
byte
[]
build
()
{
if
(
getHeader
()
==
null
)
{
this
.
header
=
new
byte
[
0
];
}
if
(
getData
()
==
null
)
{
this
.
data
=
new
byte
[
0
];
}
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
(
2
+
2
+
2
+
4
+
getHeader
().
length
+
getData
().
length
+
2
);
this
.
writeUint16
(
baos
,
const1
);
this
.
writeUint16
(
baos
,
opcode
);
this
.
writeUint16
(
baos
,
header
.
length
);
this
.
writeUint32
(
baos
,
data
.
length
);
this
.
writeBytes
(
baos
,
header
);
this
.
writeBytes
(
baos
,
data
);
this
.
writeUint16
(
baos
,
const2
);
byte
[]
packet
=
baos
.
toByteArray
();
if
(
this
.
shouldEncrypt
)
{
Crypto
.
xor
(
packet
,
this
.
useDispatchKey
()
?
Crypto
.
DISPATCH_KEY
:
Crypto
.
ENCRYPT_KEY
);
}
return
packet
;
}
public
void
writeUint16
(
ByteArrayOutputStream
baos
,
int
i
)
{
// Unsigned short
baos
.
write
((
byte
)
((
i
>>>
8
)
&
0xFF
));
baos
.
write
((
byte
)
(
i
&
0xFF
));
}
public
void
writeUint32
(
ByteArrayOutputStream
baos
,
int
i
)
{
// Unsigned int (long)
public
void
writeUint32
(
ByteArrayOutputStream
baos
,
int
i
)
{
// Unsigned int (long)
baos
.
write
((
byte
)
((
i
>>>
24
)
&
0xFF
));
baos
.
write
((
byte
)
((
i
>>>
16
)
&
0xFF
));
baos
.
write
((
byte
)
((
i
>>>
8
)
&
0xFF
));
baos
.
write
((
byte
)
(
i
&
0xFF
));
}
public
void
writeBytes
(
ByteArrayOutputStream
baos
,
byte
[]
bytes
)
{
try
{
baos
.
write
(
bytes
);
}
catch
(
IOException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
public
void
writeBytes
(
ByteArrayOutputStream
baos
,
byte
[]
bytes
)
{
try
{
baos
.
write
(
bytes
);
}
catch
(
IOException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
}
}
src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java
View file @
ae2d1fe4
...
...
@@ -1854,4 +1854,4 @@ public class PacketOpcodes {
public
static
final
int
WorldRoutineTypeCloseNotify
=
3502
;
public
static
final
int
WorldRoutineTypeRefreshNotify
=
3525
;
}
\ No newline at end of file
}
src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtils.java
View file @
ae2d1fe4
...
...
@@ -22,7 +22,7 @@ public class PacketOpcodesUtils {
PacketOpcodes
.
WindSeedClientNotify
,
PacketOpcodes
.
PlayerLuaShellNotify
);
public
static
final
Set
<
Integer
>
LOOP_PACKETS
=
Set
.
of
(
PacketOpcodes
.
PingReq
,
PacketOpcodes
.
PingRsp
,
...
...
@@ -30,39 +30,39 @@ public class PacketOpcodesUtils {
PacketOpcodes
.
UnionCmdNotify
,
PacketOpcodes
.
QueryPathReq
);
static
{
opcodeMap
=
new
Int2ObjectOpenHashMap
<
String
>();
Field
[]
fields
=
PacketOpcodes
.
class
.
getFields
();
static
{
opcodeMap
=
new
Int2ObjectOpenHashMap
<
String
>();
Field
[]
fields
=
PacketOpcodes
.
class
.
getFields
();
for
(
Field
f
:
fields
)
{
if
(
f
.
getType
().
equals
(
int
.
class
))
{
try
{
opcodeMap
.
put
(
f
.
getInt
(
null
),
f
.
getName
());
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
}
}
for
(
Field
f
:
fields
)
{
if
(
f
.
getType
().
equals
(
int
.
class
))
{
try
{
opcodeMap
.
put
(
f
.
getInt
(
null
),
f
.
getName
());
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
}
}
public
static
String
getOpcodeName
(
int
opcode
)
{
if
(
opcode
<=
0
)
return
"UNKNOWN"
;
return
opcodeMap
.
getOrDefault
(
opcode
,
"UNKNOWN"
);
}
public
static
String
getOpcodeName
(
int
opcode
)
{
if
(
opcode
<=
0
)
return
"UNKNOWN"
;
return
opcodeMap
.
getOrDefault
(
opcode
,
"UNKNOWN"
);
}
public
static
void
dumpPacketIds
()
{
try
(
FileWriter
writer
=
new
FileWriter
(
"./PacketIds_"
+
GameConstants
.
VERSION
+
".json"
))
{
// Create sorted tree map
Map
<
Integer
,
String
>
packetIds
=
opcodeMap
.
int2ObjectEntrySet
().
stream
()
.
filter
(
e
->
e
.
getIntKey
()
>
0
)
.
collect
(
Collectors
.
toMap
(
Int2ObjectMap
.
Entry
::
getIntKey
,
Int2ObjectMap
.
Entry
::
getValue
,
(
k
,
v
)
->
v
,
TreeMap:
:
new
));
// Write to file
writer
.
write
(
Grasscutter
.
getGsonFactory
().
toJson
(
packetIds
));
Grasscutter
.
getLogger
().
info
(
"Dumped packet ids."
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
public
static
void
dumpPacketIds
()
{
try
(
FileWriter
writer
=
new
FileWriter
(
"./PacketIds_"
+
GameConstants
.
VERSION
+
".json"
))
{
// Create sorted tree map
Map
<
Integer
,
String
>
packetIds
=
opcodeMap
.
int2ObjectEntrySet
().
stream
()
.
filter
(
e
->
e
.
getIntKey
()
>
0
)
.
collect
(
Collectors
.
toMap
(
Int2ObjectMap
.
Entry
::
getIntKey
,
Int2ObjectMap
.
Entry
::
getValue
,
(
k
,
v
)
->
v
,
TreeMap:
:
new
));
// Write to file
writer
.
write
(
Grasscutter
.
getGsonFactory
().
toJson
(
packetIds
));
Grasscutter
.
getLogger
().
info
(
"Dumped packet ids."
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
}
src/main/java/emu/grasscutter/plugin/Plugin.java
View file @
ae2d1fe4
...
...
@@ -17,7 +17,7 @@ import java.net.URLClassLoader;
*/
public
abstract
class
Plugin
{
private
final
ServerHook
server
=
ServerHook
.
getInstance
();
private
PluginIdentifier
identifier
;
private
URLClassLoader
classLoader
;
private
File
dataFolder
;
...
...
@@ -25,22 +25,22 @@ public abstract class Plugin {
/**
* This method is reflected into.
*
*
* Set plugin variables.
* @param identifier The plugin's identifier.
*/
private
void
initializePlugin
(
PluginIdentifier
identifier
,
URLClassLoader
classLoader
)
{
if
(
this
.
identifier
!=
null
)
{
if
(
this
.
identifier
!=
null
)
{
Grasscutter
.
getLogger
().
warn
(
this
.
identifier
.
name
+
" had a reinitialization attempt."
);
return
;
}
this
.
identifier
=
identifier
;
this
.
classLoader
=
classLoader
;
this
.
dataFolder
=
new
File
(
PLUGIN
(),
identifier
.
name
);
this
.
logger
=
LoggerFactory
.
getLogger
(
identifier
.
name
);
if
(!
this
.
dataFolder
.
exists
()
&&
!
this
.
dataFolder
.
mkdirs
())
{
if
(!
this
.
dataFolder
.
exists
()
&&
!
this
.
dataFolder
.
mkdirs
())
{
Grasscutter
.
getLogger
().
warn
(
"Failed to create plugin data folder for "
+
this
.
identifier
.
name
);
return
;
}
...
...
@@ -50,7 +50,7 @@ public abstract class Plugin {
* The plugin's identifier instance.
* @return An instance of {@link PluginIdentifier}.
*/
public
final
PluginIdentifier
getIdentifier
(){
public
final
PluginIdentifier
getIdentifier
()
{
return
this
.
identifier
;
}
...
...
@@ -115,7 +115,7 @@ public abstract class Plugin {
public
final
Logger
getLogger
()
{
return
this
.
logger
;
}
/* Called when the plugin is first loaded. */
public
void
onLoad
()
{
}
/* Called after (most of) the server enables. */
...
...
src/main/java/emu/grasscutter/plugin/PluginManager.java
View file @
ae2d1fe4
...
...
@@ -72,7 +72,7 @@ public final class PluginManager {
List
<
PluginData
>
dependencies
=
new
ArrayList
<>();
// Initialize all plugins.
for
(
var
plugin
:
plugins
)
{
for
(
var
plugin
:
plugins
)
{
try
{
URL
url
=
plugin
.
toURI
().
toURL
();
try
(
URLClassLoader
loader
=
new
URLClassLoader
(
new
URL
[]{
url
}))
{
...
...
@@ -109,7 +109,7 @@ public final class PluginManager {
fileReader
.
close
();
// Check if the plugin has alternate dependencies.
if
(
pluginConfig
.
loadAfter
!=
null
&&
pluginConfig
.
loadAfter
.
length
>
0
)
{
if
(
pluginConfig
.
loadAfter
!=
null
&&
pluginConfig
.
loadAfter
.
length
>
0
)
{
// Add the plugin to a "load later" list.
dependencies
.
add
(
new
PluginData
(
pluginInstance
,
PluginIdentifier
.
fromPluginConfig
(
pluginConfig
),
...
...
@@ -131,9 +131,9 @@ public final class PluginManager {
// Load plugins with dependencies.
int
depth
=
0
;
final
int
maxDepth
=
30
;
while
(!
dependencies
.
isEmpty
())
{
while
(!
dependencies
.
isEmpty
())
{
// Check if the depth is too high.
if
(
depth
>=
maxDepth
)
{
if
(
depth
>=
maxDepth
)
{
Grasscutter
.
getLogger
().
error
(
"Failed to load plugins with dependencies."
);
break
;
}
...
...
@@ -143,7 +143,7 @@ public final class PluginManager {
var
pluginData
=
dependencies
.
get
(
0
);
// Check if the plugin's dependencies are loaded.
if
(!
this
.
plugins
.
keySet
().
containsAll
(
List
.
of
(
pluginData
.
getDependencies
())))
{
if
(!
this
.
plugins
.
keySet
().
containsAll
(
List
.
of
(
pluginData
.
getDependencies
())))
{
depth
++;
// Increase depth counter.
continue
;
// Continue to next plugin.
}
...
...
src/main/java/emu/grasscutter/plugin/api/PlayerHook.java
View file @
ae2d1fe4
...
...
@@ -19,13 +19,13 @@ public final class PlayerHook {
private
final
Player
player
;
/**
* Hooks into the player.
* Hooks into the player.
* @param player The player to hook into.
*/
public
PlayerHook
(
Player
player
)
{
this
.
player
=
player
;
}
/**
* Kicks a player from the server.
* TODO: Refactor to kick using a packet.
...
...
@@ -82,7 +82,7 @@ public final class PlayerHook {
*/
public
void
teleport
(
Position
position
)
{
this
.
player
.
getPosition
().
set
(
position
);
this
.
player
.
sendPacket
(
new
PacketPlayerEnterSceneNotify
(
this
.
player
,
this
.
player
.
sendPacket
(
new
PacketPlayerEnterSceneNotify
(
this
.
player
,
EnterType
.
ENTER_TYPE_JUMP
,
EnterReason
.
TransPoint
,
this
.
player
.
getSceneId
(),
position
));
...
...
@@ -111,4 +111,4 @@ public final class PlayerHook {
public
Avatar
getCurrentAvatar
()
{
return
this
.
getCurrentAvatarEntity
().
getAvatar
();
}
}
\ No newline at end of file
}
src/main/java/emu/grasscutter/scripts/data/SceneBlock.java
View file @
ae2d1fe4
...
...
@@ -22,61 +22,61 @@ import java.util.stream.Collectors;
@ToString
@Setter
public
class
SceneBlock
{
public
int
id
;
public
Position
max
;
public
Position
min
;
public
int
sceneId
;
public
Map
<
Integer
,
SceneGroup
>
groups
;
public
RTree
<
SceneGroup
,
Geometry
>
sceneGroupIndex
;
private
transient
boolean
loaded
;
// Not an actual variable in the scripts either
public
boolean
isLoaded
()
{
return
this
.
loaded
;
}
public
void
setLoaded
(
boolean
loaded
)
{
this
.
loaded
=
loaded
;
}
public
boolean
contains
(
Position
pos
)
{
return
pos
.
getX
()
<=
this
.
max
.
getX
()
&&
pos
.
getX
()
>=
this
.
min
.
getX
()
&&
pos
.
getZ
()
<=
this
.
max
.
getZ
()
&&
pos
.
getZ
()
>=
this
.
min
.
getZ
();
}
public
SceneBlock
load
(
int
sceneId
,
Bindings
bindings
){
if
(
this
.
loaded
){
return
this
;
}
this
.
sceneId
=
sceneId
;
public
int
id
;
public
Position
max
;
public
Position
min
;
public
int
sceneId
;
public
Map
<
Integer
,
SceneGroup
>
groups
;
public
RTree
<
SceneGroup
,
Geometry
>
sceneGroupIndex
;
private
transient
boolean
loaded
;
// Not an actual variable in the scripts either
public
boolean
isLoaded
()
{
return
this
.
loaded
;
}
public
void
setLoaded
(
boolean
loaded
)
{
this
.
loaded
=
loaded
;
}
public
boolean
contains
(
Position
pos
)
{
return
pos
.
getX
()
<=
this
.
max
.
getX
()
&&
pos
.
getX
()
>=
this
.
min
.
getX
()
&&
pos
.
getZ
()
<=
this
.
max
.
getZ
()
&&
pos
.
getZ
()
>=
this
.
min
.
getZ
();
}
public
SceneBlock
load
(
int
sceneId
,
Bindings
bindings
)
{
if
(
this
.
loaded
)
{
return
this
;
}
this
.
sceneId
=
sceneId
;
this
.
setLoaded
(
true
);
CompiledScript
cs
=
ScriptLoader
.
getScriptByPath
(
SCRIPT
(
"Scene/"
+
sceneId
+
"/scene"
+
sceneId
+
"_block"
+
this
.
id
+
"."
+
ScriptLoader
.
getScriptType
()));
CompiledScript
cs
=
ScriptLoader
.
getScriptByPath
(
SCRIPT
(
"Scene/"
+
sceneId
+
"/scene"
+
sceneId
+
"_block"
+
this
.
id
+
"."
+
ScriptLoader
.
getScriptType
()));
if
(
cs
==
null
)
{
return
null
;
}
if
(
cs
==
null
)
{
return
null
;
}
// Eval script
try
{
cs
.
eval
(
bindings
);
// Eval script
try
{
cs
.
eval
(
bindings
);
// Set groups
// Set groups
this
.
groups
=
ScriptLoader
.
getSerializer
().
toList
(
SceneGroup
.
class
,
bindings
.
get
(
"groups"
)).
stream
()
.
collect
(
Collectors
.
toMap
(
x
->
x
.
id
,
y
->
y
));
.
collect
(
Collectors
.
toMap
(
x
->
x
.
id
,
y
->
y
));
this
.
groups
.
values
().
forEach
(
g
->
g
.
block_id
=
this
.
id
);
this
.
sceneGroupIndex
=
SceneIndexManager
.
buildIndex
(
3
,
this
.
groups
.
values
(),
g
->
g
.
pos
.
toPoint
());
}
catch
(
ScriptException
exception
)
{
this
.
sceneGroupIndex
=
SceneIndexManager
.
buildIndex
(
3
,
this
.
groups
.
values
(),
g
->
g
.
pos
.
toPoint
());
}
catch
(
ScriptException
exception
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while loading block "
+
this
.
id
+
" in scene "
+
sceneId
,
exception
);
}
Grasscutter
.
getLogger
().
debug
(
"Successfully loaded block {} in scene {}."
,
this
.
id
,
sceneId
);
return
this
;
}
public
Rectangle
toRectangle
()
{
return
Rectangle
.
create
(
this
.
min
.
toXZDoubleArray
(),
this
.
max
.
toXZDoubleArray
());
}
}
\ No newline at end of file
}
Grasscutter
.
getLogger
().
debug
(
"Successfully loaded block {} in scene {}."
,
this
.
id
,
sceneId
);
return
this
;
}
public
Rectangle
toRectangle
()
{
return
Rectangle
.
create
(
this
.
min
.
toXZDoubleArray
(),
this
.
max
.
toXZDoubleArray
());
}
}
src/main/java/emu/grasscutter/scripts/data/SceneGroup.java
View file @
ae2d1fe4
...
...
@@ -21,93 +21,93 @@ import java.util.stream.Collectors;
@ToString
@Setter
public
class
SceneGroup
{
public
transient
int
block_id
;
// Not an actual variable in the scripts but we will keep it here for reference
public
transient
int
block_id
;
// Not an actual variable in the scripts but we will keep it here for reference
public
int
id
;
public
int
refresh_id
;
public
Position
pos
;
public
int
id
;
public
int
refresh_id
;
public
Position
pos
;
public
Map
<
Integer
,
SceneMonster
>
monsters
;
// <ConfigId, Monster>
public
Map
<
Integer
,
SceneGadget
>
gadgets
;
// <ConfigId, Gadgets>
public
Map
<
String
,
SceneTrigger
>
triggers
;
public
Map
<
Integer
,
SceneMonster
>
monsters
;
// <ConfigId, Monster>
public
Map
<
Integer
,
SceneGadget
>
gadgets
;
// <ConfigId, Gadgets>
public
Map
<
String
,
SceneTrigger
>
triggers
;
public
Map
<
Integer
,
SceneRegion
>
regions
;
public
List
<
SceneSuite
>
suites
;
public
List
<
SceneVar
>
variables
;
public
SceneBusiness
business
;
public
SceneGarbage
garbages
;
public
SceneInitConfig
init_config
;
private
transient
boolean
loaded
;
// Not an actual variable in the scripts either
private
transient
CompiledScript
script
;
private
transient
Bindings
bindings
;
public
static
SceneGroup
of
(
int
groupId
)
{
var
group
=
new
SceneGroup
();
group
.
id
=
groupId
;
return
group
;
}
public
boolean
isLoaded
()
{
return
this
.
loaded
;
}
public
void
setLoaded
(
boolean
loaded
)
{
this
.
loaded
=
loaded
;
}
public
int
getBusinessType
()
{
return
this
.
business
==
null
?
0
:
this
.
business
.
type
;
}
public
List
<
SceneGadget
>
getGarbageGadgets
()
{
return
this
.
garbages
==
null
?
null
:
this
.
garbages
.
gadgets
;
}
public
CompiledScript
getScript
()
{
return
this
.
script
;
}
public
SceneSuite
getSuiteByIndex
(
int
index
)
{
return
this
.
suites
.
get
(
index
-
1
);
}
public
Bindings
getBindings
()
{
return
this
.
bindings
;
}
public
synchronized
SceneGroup
load
(
int
sceneId
){
if
(
this
.
loaded
){
return
this
;
}
// Set flag here so if there is no script, we don't call this function over and over again.
public
List
<
SceneSuite
>
suites
;
public
List
<
SceneVar
>
variables
;
public
SceneBusiness
business
;
public
SceneGarbage
garbages
;
public
SceneInitConfig
init_config
;
private
transient
boolean
loaded
;
// Not an actual variable in the scripts either
private
transient
CompiledScript
script
;
private
transient
Bindings
bindings
;
public
static
SceneGroup
of
(
int
groupId
)
{
var
group
=
new
SceneGroup
();
group
.
id
=
groupId
;
return
group
;
}
public
boolean
isLoaded
()
{
return
this
.
loaded
;
}
public
void
setLoaded
(
boolean
loaded
)
{
this
.
loaded
=
loaded
;
}
public
int
getBusinessType
()
{
return
this
.
business
==
null
?
0
:
this
.
business
.
type
;
}
public
List
<
SceneGadget
>
getGarbageGadgets
()
{
return
this
.
garbages
==
null
?
null
:
this
.
garbages
.
gadgets
;
}
public
CompiledScript
getScript
()
{
return
this
.
script
;
}
public
SceneSuite
getSuiteByIndex
(
int
index
)
{
return
this
.
suites
.
get
(
index
-
1
);
}
public
Bindings
getBindings
()
{
return
this
.
bindings
;
}
public
synchronized
SceneGroup
load
(
int
sceneId
)
{
if
(
this
.
loaded
)
{
return
this
;
}
// Set flag here so if there is no script, we don't call this function over and over again.
this
.
setLoaded
(
true
);
this
.
bindings
=
ScriptLoader
.
getEngine
().
createBindings
();
this
.
bindings
=
ScriptLoader
.
getEngine
().
createBindings
();
CompiledScript
cs
=
ScriptLoader
.
getScriptByPath
(
SCRIPT
(
"Scene/"
+
sceneId
+
"/scene"
+
sceneId
+
"_group"
+
this
.
id
+
"."
+
ScriptLoader
.
getScriptType
()));
CompiledScript
cs
=
ScriptLoader
.
getScriptByPath
(
SCRIPT
(
"Scene/"
+
sceneId
+
"/scene"
+
sceneId
+
"_group"
+
this
.
id
+
"."
+
ScriptLoader
.
getScriptType
()));
if
(
cs
==
null
)
{
return
this
;
}
if
(
cs
==
null
)
{
return
this
;
}
this
.
script
=
cs
;
this
.
script
=
cs
;
// Eval script
try
{
cs
.
eval
(
this
.
bindings
);
// Eval script
try
{
cs
.
eval
(
this
.
bindings
);
// Set
// Set
this
.
monsters
=
ScriptLoader
.
getSerializer
().
toList
(
SceneMonster
.
class
,
this
.
bindings
.
get
(
"monsters"
)).
stream
()
.
collect
(
Collectors
.
toMap
(
x
->
x
.
config_id
,
y
->
y
));
.
collect
(
Collectors
.
toMap
(
x
->
x
.
config_id
,
y
->
y
));
this
.
monsters
.
values
().
forEach
(
m
->
m
.
group
=
this
);
this
.
gadgets
=
ScriptLoader
.
getSerializer
().
toList
(
SceneGadget
.
class
,
this
.
bindings
.
get
(
"gadgets"
)).
stream
()
.
collect
(
Collectors
.
toMap
(
x
->
x
.
config_id
,
y
->
y
));
.
collect
(
Collectors
.
toMap
(
x
->
x
.
config_id
,
y
->
y
));
this
.
gadgets
.
values
().
forEach
(
m
->
m
.
group
=
this
);
this
.
triggers
=
ScriptLoader
.
getSerializer
().
toList
(
SceneTrigger
.
class
,
this
.
bindings
.
get
(
"triggers"
)).
stream
()
.
collect
(
Collectors
.
toMap
(
x
->
x
.
name
,
y
->
y
));
.
collect
(
Collectors
.
toMap
(
x
->
x
.
name
,
y
->
y
));
this
.
triggers
.
values
().
forEach
(
t
->
t
.
currentGroup
=
this
);
this
.
suites
=
ScriptLoader
.
getSerializer
().
toList
(
SceneSuite
.
class
,
this
.
bindings
.
get
(
"suites"
));
...
...
@@ -117,35 +117,35 @@ public class SceneGroup {
this
.
init_config
=
ScriptLoader
.
getSerializer
().
toObject
(
SceneInitConfig
.
class
,
this
.
bindings
.
get
(
"init_config"
));
// Garbages // TODO: fix properly later
Object
garbagesValue
=
this
.
bindings
.
get
(
"garbages"
);
if
(
garbagesValue
instanceof
LuaValue
garbagesTable
)
{
// Garbages // TODO: fix properly later
Object
garbagesValue
=
this
.
bindings
.
get
(
"garbages"
);
if
(
garbagesValue
instanceof
LuaValue
garbagesTable
)
{
this
.
garbages
=
new
SceneGarbage
();
if
(
garbagesTable
.
checktable
().
get
(
"gadgets"
)
!=
LuaValue
.
NIL
)
{
if
(
garbagesTable
.
checktable
().
get
(
"gadgets"
)
!=
LuaValue
.
NIL
)
{
this
.
garbages
.
gadgets
=
ScriptLoader
.
getSerializer
().
toList
(
SceneGadget
.
class
,
garbagesTable
.
checktable
().
get
(
"gadgets"
).
checktable
());
this
.
garbages
.
gadgets
.
forEach
(
m
->
m
.
group
=
this
);
}
}
}
}
// Add variables to suite
this
.
variables
=
ScriptLoader
.
getSerializer
().
toList
(
SceneVar
.
class
,
this
.
bindings
.
get
(
"variables"
));
// Add variables to suite
this
.
variables
=
ScriptLoader
.
getSerializer
().
toList
(
SceneVar
.
class
,
this
.
bindings
.
get
(
"variables"
));
// Add monsters and gadgets to suite
// Add monsters and gadgets to suite
this
.
suites
.
forEach
(
i
->
i
.
init
(
this
));
}
catch
(
ScriptException
e
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while loading group "
+
this
.
id
+
" in scene "
+
sceneId
+
"."
,
e
);
}
}
catch
(
ScriptException
e
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while loading group "
+
this
.
id
+
" in scene "
+
sceneId
+
"."
,
e
);
}
Grasscutter
.
getLogger
().
debug
(
"Successfully loaded group {} in scene {}."
,
this
.
id
,
sceneId
);
return
this
;
}
Grasscutter
.
getLogger
().
debug
(
"Successfully loaded group {} in scene {}."
,
this
.
id
,
sceneId
);
return
this
;
}
public
Optional
<
SceneBossChest
>
searchBossChestInGroup
()
{
return
this
.
gadgets
.
values
().
stream
()
.
filter
(
g
->
g
.
boss_chest
!=
null
&&
g
.
boss_chest
.
monster_config_id
>
0
)
.
map
(
g
->
g
.
boss_chest
)
.
findFirst
();
}
public
Optional
<
SceneBossChest
>
searchBossChestInGroup
()
{
return
this
.
gadgets
.
values
().
stream
()
.
filter
(
g
->
g
.
boss_chest
!=
null
&&
g
.
boss_chest
.
monster_config_id
>
0
)
.
map
(
g
->
g
.
boss_chest
)
.
findFirst
();
}
}
src/main/java/emu/grasscutter/scripts/data/SceneMeta.java
View file @
ae2d1fe4
...
...
@@ -33,7 +33,7 @@ public class SceneMeta {
return
new
SceneMeta
().
load
(
sceneId
);
}
public
SceneMeta
load
(
int
sceneId
){
public
SceneMeta
load
(
int
sceneId
)
{
// Get compiled script if cached
CompiledScript
cs
=
ScriptLoader
.
getScriptByPath
(
SCRIPT
(
"Scene/"
+
sceneId
+
"/scene"
+
sceneId
+
"."
+
ScriptLoader
.
getScriptType
()));
...
...
src/main/java/emu/grasscutter/server/game/BaseGameSystem.java
View file @
ae2d1fe4
...
...
@@ -2,11 +2,11 @@ package emu.grasscutter.server.game;
public
abstract
class
BaseGameSystem
{
protected
final
GameServer
server
;
public
BaseGameSystem
(
GameServer
server
)
{
this
.
server
=
server
;
}
public
GameServer
getServer
()
{
return
this
.
server
;
}
...
...
src/main/java/emu/grasscutter/server/game/GameServer.java
View file @
ae2d1fe4
...
...
@@ -51,97 +51,97 @@ import static emu.grasscutter.utils.Language.translate;
public
final
class
GameServer
extends
KcpServer
{
// Game server base
private
final
InetSocketAddress
address
;
private
final
GameServerPacketHandler
packetHandler
;
private
final
GameServerPacketHandler
packetHandler
;
private
final
Map
<
Integer
,
Player
>
players
;
private
final
Set
<
World
>
worlds
;
// Server systems
private
final
InventorySystem
inventorySystem
;
private
final
GachaSystem
gachaSystem
;
private
final
ShopSystem
shopSystem
;
private
final
MultiplayerSystem
multiplayerSystem
;
private
final
DungeonSystem
dungeonSystem
;
private
final
ExpeditionSystem
expeditionSystem
;
private
final
DropSystem
dropSystem
;
private
final
WorldDataSystem
worldDataSystem
;
private
final
BattlePassSystem
battlePassSystem
;
private
final
CombineManger
combineSystem
;
private
final
TowerSystem
towerSystem
;
private
final
AnnouncementSystem
announcementSystem
;
private
final
QuestSystem
questSystem
;
// Extra
private
final
ServerTaskScheduler
scheduler
;
private
final
InventorySystem
inventorySystem
;
private
final
GachaSystem
gachaSystem
;
private
final
ShopSystem
shopSystem
;
private
final
MultiplayerSystem
multiplayerSystem
;
private
final
DungeonSystem
dungeonSystem
;
private
final
ExpeditionSystem
expeditionSystem
;
private
final
DropSystem
dropSystem
;
private
final
WorldDataSystem
worldDataSystem
;
private
final
BattlePassSystem
battlePassSystem
;
private
final
CombineManger
combineSystem
;
private
final
TowerSystem
towerSystem
;
private
final
AnnouncementSystem
announcementSystem
;
private
final
QuestSystem
questSystem
;
// Extra
private
final
ServerTaskScheduler
scheduler
;
private
final
CommandMap
commandMap
;
private
final
TaskMap
taskMap
;
private
ChatManagerHandler
chatManager
;
public
GameServer
()
{
this
(
getAdapterInetSocketAddress
());
}
public
GameServer
(
InetSocketAddress
address
)
{
ChannelConfig
channelConfig
=
new
ChannelConfig
();
channelConfig
.
nodelay
(
true
,
40
,
2
,
true
);
channelConfig
.
setMtu
(
1400
);
channelConfig
.
setSndwnd
(
256
);
channelConfig
.
setRcvwnd
(
256
);
channelConfig
.
setTimeoutMillis
(
30
*
1000
);
//30s
channelConfig
.
setUseConvChannel
(
true
);
channelConfig
.
setAckNoDelay
(
false
);
this
.
init
(
GameSessionManager
.
getListener
(),
channelConfig
,
address
);
DungeonChallenge
.
initialize
();
EnergyManager
.
initialize
();
StaminaManager
.
initialize
();
CookingManager
.
initialize
();
CombineManger
.
initialize
();
// Game Server base
this
.
address
=
address
;
this
.
packetHandler
=
new
GameServerPacketHandler
(
PacketHandler
.
class
);
this
.
players
=
new
ConcurrentHashMap
<>();
this
.
worlds
=
Collections
.
synchronizedSet
(
new
HashSet
<>());
// Extra
this
.
scheduler
=
new
ServerTaskScheduler
();
this
.
commandMap
=
new
CommandMap
(
true
);
private
ChatManagerHandler
chatManager
;
public
GameServer
()
{
this
(
getAdapterInetSocketAddress
());
}
public
GameServer
(
InetSocketAddress
address
)
{
ChannelConfig
channelConfig
=
new
ChannelConfig
();
channelConfig
.
nodelay
(
true
,
40
,
2
,
true
);
channelConfig
.
setMtu
(
1400
);
channelConfig
.
setSndwnd
(
256
);
channelConfig
.
setRcvwnd
(
256
);
channelConfig
.
setTimeoutMillis
(
30
*
1000
);
//30s
channelConfig
.
setUseConvChannel
(
true
);
channelConfig
.
setAckNoDelay
(
false
);
this
.
init
(
GameSessionManager
.
getListener
(),
channelConfig
,
address
);
DungeonChallenge
.
initialize
();
EnergyManager
.
initialize
();
StaminaManager
.
initialize
();
CookingManager
.
initialize
();
CombineManger
.
initialize
();
// Game Server base
this
.
address
=
address
;
this
.
packetHandler
=
new
GameServerPacketHandler
(
PacketHandler
.
class
);
this
.
players
=
new
ConcurrentHashMap
<>();
this
.
worlds
=
Collections
.
synchronizedSet
(
new
HashSet
<>());
// Extra
this
.
scheduler
=
new
ServerTaskScheduler
();
this
.
commandMap
=
new
CommandMap
(
true
);
this
.
taskMap
=
new
TaskMap
(
true
);
// Create game systems
this
.
inventorySystem
=
new
InventorySystem
(
this
);
this
.
gachaSystem
=
new
GachaSystem
(
this
);
this
.
shopSystem
=
new
ShopSystem
(
this
);
this
.
multiplayerSystem
=
new
MultiplayerSystem
(
this
);
this
.
dungeonSystem
=
new
DungeonSystem
(
this
);
this
.
dropSystem
=
new
DropSystem
(
this
);
this
.
expeditionSystem
=
new
ExpeditionSystem
(
this
);
this
.
combineSystem
=
new
CombineManger
(
this
);
this
.
towerSystem
=
new
TowerSystem
(
this
);
this
.
worldDataSystem
=
new
WorldDataSystem
(
this
);
this
.
battlePassSystem
=
new
BattlePassSystem
(
this
);
this
.
announcementSystem
=
new
AnnouncementSystem
(
this
);
this
.
questSystem
=
new
QuestSystem
(
this
);
// Chata manager
this
.
chatManager
=
new
ChatManager
(
this
);
// Hook into shutdown event.
Runtime
.
getRuntime
().
addShutdownHook
(
new
Thread
(
this
::
onServerShutdown
));
}
// Create game systems
this
.
inventorySystem
=
new
InventorySystem
(
this
);
this
.
gachaSystem
=
new
GachaSystem
(
this
);
this
.
shopSystem
=
new
ShopSystem
(
this
);
this
.
multiplayerSystem
=
new
MultiplayerSystem
(
this
);
this
.
dungeonSystem
=
new
DungeonSystem
(
this
);
this
.
dropSystem
=
new
DropSystem
(
this
);
this
.
expeditionSystem
=
new
ExpeditionSystem
(
this
);
this
.
combineSystem
=
new
CombineManger
(
this
);
this
.
towerSystem
=
new
TowerSystem
(
this
);
this
.
worldDataSystem
=
new
WorldDataSystem
(
this
);
this
.
battlePassSystem
=
new
BattlePassSystem
(
this
);
this
.
announcementSystem
=
new
AnnouncementSystem
(
this
);
this
.
questSystem
=
new
QuestSystem
(
this
);
// Chata manager
this
.
chatManager
=
new
ChatManager
(
this
);
// Hook into shutdown event.
Runtime
.
getRuntime
().
addShutdownHook
(
new
Thread
(
this
::
onServerShutdown
));
}
@Deprecated
public
ChatManagerHandler
getChatManager
()
{
return
chatManager
;
}
@Deprecated
public
void
setChatManager
(
ChatManagerHandler
chatManager
)
{
this
.
chatManager
=
chatManager
;
}
public
ChatManagerHandler
getChatSystem
()
{
return
chatManager
;
}
...
...
@@ -150,71 +150,71 @@ public final class GameServer extends KcpServer {
this
.
chatManager
=
chatManager
;
}
private
static
InetSocketAddress
getAdapterInetSocketAddress
(){
InetSocketAddress
inetSocketAddress
;
if
(
GAME_INFO
.
bindAddress
.
equals
(
""
)){
inetSocketAddress
=
new
InetSocketAddress
(
GAME_INFO
.
bindPort
);
}
else
{
inetSocketAddress
=
new
InetSocketAddress
(
GAME_INFO
.
bindAddress
,
GAME_INFO
.
bindPort
);
}
return
inetSocketAddress
;
}
public
void
registerPlayer
(
Player
player
)
{
getPlayers
().
put
(
player
.
getUid
(),
player
);
}
public
Player
getPlayerByUid
(
int
id
)
{
return
this
.
getPlayerByUid
(
id
,
false
);
}
public
Player
getPlayerByUid
(
int
id
,
boolean
allowOfflinePlayers
)
{
// Console check
if
(
id
==
GameConstants
.
SERVER_CONSOLE_UID
)
{
return
null
;
}
// Get from online players
Player
player
=
this
.
getPlayers
().
get
(
id
);
if
(!
allowOfflinePlayers
)
{
return
player
;
}
// Check database if character isnt here
if
(
player
==
null
)
{
player
=
DatabaseHelper
.
getPlayerByUid
(
id
);
}
return
player
;
}
public
Player
getPlayerByAccountId
(
String
accountId
)
{
Optional
<
Player
>
playerOpt
=
getPlayers
().
values
().
stream
().
filter
(
player
->
player
.
getAccount
().
getId
().
equals
(
accountId
)).
findFirst
();
return
playerOpt
.
orElse
(
null
);
}
public
SocialDetail
.
Builder
getSocialDetailByUid
(
int
id
)
{
// Get from online players
Player
player
=
this
.
getPlayerByUid
(
id
,
true
);
if
(
player
==
null
)
{
return
null
;
}
return
player
.
getSocialDetail
();
}
public
Account
getAccountByName
(
String
username
)
{
Optional
<
Player
>
playerOpt
=
getPlayers
().
values
().
stream
().
filter
(
player
->
player
.
getAccount
().
getUsername
().
equals
(
username
)).
findFirst
();
if
(
playerOpt
.
isPresent
())
{
return
playerOpt
.
get
().
getAccount
();
}
return
DatabaseHelper
.
getAccountByName
(
username
);
}
private
static
InetSocketAddress
getAdapterInetSocketAddress
()
{
InetSocketAddress
inetSocketAddress
;
if
(
GAME_INFO
.
bindAddress
.
equals
(
""
))
{
inetSocketAddress
=
new
InetSocketAddress
(
GAME_INFO
.
bindPort
);
}
else
{
inetSocketAddress
=
new
InetSocketAddress
(
GAME_INFO
.
bindAddress
,
GAME_INFO
.
bindPort
);
}
return
inetSocketAddress
;
}
public
void
registerPlayer
(
Player
player
)
{
getPlayers
().
put
(
player
.
getUid
(),
player
);
}
public
Player
getPlayerByUid
(
int
id
)
{
return
this
.
getPlayerByUid
(
id
,
false
);
}
public
Player
getPlayerByUid
(
int
id
,
boolean
allowOfflinePlayers
)
{
// Console check
if
(
id
==
GameConstants
.
SERVER_CONSOLE_UID
)
{
return
null
;
}
// Get from online players
Player
player
=
this
.
getPlayers
().
get
(
id
);
if
(!
allowOfflinePlayers
)
{
return
player
;
}
// Check database if character isnt here
if
(
player
==
null
)
{
player
=
DatabaseHelper
.
getPlayerByUid
(
id
);
}
return
player
;
}
public
Player
getPlayerByAccountId
(
String
accountId
)
{
Optional
<
Player
>
playerOpt
=
getPlayers
().
values
().
stream
().
filter
(
player
->
player
.
getAccount
().
getId
().
equals
(
accountId
)).
findFirst
();
return
playerOpt
.
orElse
(
null
);
}
public
SocialDetail
.
Builder
getSocialDetailByUid
(
int
id
)
{
// Get from online players
Player
player
=
this
.
getPlayerByUid
(
id
,
true
);
if
(
player
==
null
)
{
return
null
;
}
return
player
.
getSocialDetail
();
}
public
Account
getAccountByName
(
String
username
)
{
Optional
<
Player
>
playerOpt
=
getPlayers
().
values
().
stream
().
filter
(
player
->
player
.
getAccount
().
getUsername
().
equals
(
username
)).
findFirst
();
if
(
playerOpt
.
isPresent
())
{
return
playerOpt
.
get
().
getAccount
();
}
return
DatabaseHelper
.
getAccountByName
(
username
);
}
public
synchronized
void
onTick
()
{
var
tickStart
=
Instant
.
now
();
...
...
@@ -244,43 +244,43 @@ public final class GameServer extends KcpServer {
event
.
call
();
}
public
void
registerWorld
(
World
world
)
{
this
.
getWorlds
().
add
(
world
);
}
public
void
deregisterWorld
(
World
world
)
{
// TODO Auto-generated method stub
}
public
void
start
()
{
// Schedule game loop.
Timer
gameLoop
=
new
Timer
();
gameLoop
.
scheduleAtFixedRate
(
new
TimerTask
()
{
@Override
public
void
run
()
{
try
{
onTick
();
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
translate
(
"messages.game.game_update_error"
),
e
);
}
}
},
new
Date
(),
1000L
);
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.status.free_software"
));
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.game.port_bind"
,
Integer
.
toString
(
address
.
getPort
())));
ServerStartEvent
event
=
new
ServerStartEvent
(
ServerEvent
.
Type
.
GAME
,
OffsetDateTime
.
now
());
event
.
call
();
}
public
void
onServerShutdown
()
{
ServerStopEvent
event
=
new
ServerStopEvent
(
ServerEvent
.
Type
.
GAME
,
OffsetDateTime
.
now
());
event
.
call
();
// Kick and save all players
List
<
Player
>
list
=
new
ArrayList
<>(
this
.
getPlayers
().
size
());
list
.
addAll
(
this
.
getPlayers
().
values
());
for
(
Player
player
:
list
)
{
player
.
getSession
().
close
();
}
}
public
void
registerWorld
(
World
world
)
{
this
.
getWorlds
().
add
(
world
);
}
public
void
deregisterWorld
(
World
world
)
{
// TODO Auto-generated method stub
}
public
void
start
()
{
// Schedule game loop.
Timer
gameLoop
=
new
Timer
();
gameLoop
.
scheduleAtFixedRate
(
new
TimerTask
()
{
@Override
public
void
run
()
{
try
{
onTick
();
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
translate
(
"messages.game.game_update_error"
),
e
);
}
}
},
new
Date
(),
1000L
);
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.status.free_software"
));
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.game.port_bind"
,
Integer
.
toString
(
address
.
getPort
())));
ServerStartEvent
event
=
new
ServerStartEvent
(
ServerEvent
.
Type
.
GAME
,
OffsetDateTime
.
now
());
event
.
call
();
}
public
void
onServerShutdown
()
{
ServerStopEvent
event
=
new
ServerStopEvent
(
ServerEvent
.
Type
.
GAME
,
OffsetDateTime
.
now
());
event
.
call
();
// Kick and save all players
List
<
Player
>
list
=
new
ArrayList
<>(
this
.
getPlayers
().
size
());
list
.
addAll
(
this
.
getPlayers
().
values
());
for
(
Player
player
:
list
)
{
player
.
getSession
().
close
();
}
}
}
src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java
View file @
ae2d1fe4
...
...
@@ -18,84 +18,84 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@SuppressWarnings
(
"unchecked"
)
public
class
GameServerPacketHandler
{
private
final
Int2ObjectMap
<
PacketHandler
>
handlers
;
public
GameServerPacketHandler
(
Class
<?
extends
PacketHandler
>
handlerClass
)
{
this
.
handlers
=
new
Int2ObjectOpenHashMap
<>();
this
.
registerHandlers
(
handlerClass
);
}
public
void
registerPacketHandler
(
Class
<?
extends
PacketHandler
>
handlerClass
)
{
try
{
Opcodes
opcode
=
handlerClass
.
getAnnotation
(
Opcodes
.
class
);
if
(
opcode
==
null
||
opcode
.
disabled
()
||
opcode
.
value
()
<=
0
)
{
return
;
}
PacketHandler
packetHandler
=
handlerClass
.
getDeclaredConstructor
().
newInstance
();
this
.
handlers
.
put
(
opcode
.
value
(),
packetHandler
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
public
void
registerHandlers
(
Class
<?
extends
PacketHandler
>
handlerClass
)
{
Reflections
reflections
=
new
Reflections
(
"emu.grasscutter.server.packet"
);
Set
<?>
handlerClasses
=
reflections
.
getSubTypesOf
(
handlerClass
);
for
(
Object
obj
:
handlerClasses
)
{
this
.
registerPacketHandler
((
Class
<?
extends
PacketHandler
>)
obj
);
}
// Debug
Grasscutter
.
getLogger
().
debug
(
"Registered "
+
this
.
handlers
.
size
()
+
" "
+
handlerClass
.
getSimpleName
()
+
"s"
);
}
public
void
handle
(
GameSession
session
,
int
opcode
,
byte
[]
header
,
byte
[]
payload
)
{
PacketHandler
handler
=
this
.
handlers
.
get
(
opcode
);
if
(
handler
!=
null
)
{
try
{
// Make sure session is ready for packets
SessionState
state
=
session
.
getState
();
if
(
opcode
==
PacketOpcodes
.
PingReq
)
{
// Always continue if packet is ping request
}
else
if
(
opcode
==
PacketOpcodes
.
GetPlayerTokenReq
)
{
if
(
state
!=
SessionState
.
WAITING_FOR_TOKEN
)
{
return
;
}
}
else
if
(
opcode
==
PacketOpcodes
.
PlayerLoginReq
)
{
if
(
state
!=
SessionState
.
WAITING_FOR_LOGIN
)
{
return
;
}
}
else
if
(
opcode
==
PacketOpcodes
.
SetPlayerBornDataReq
)
{
if
(
state
!=
SessionState
.
PICKING_CHARACTER
)
{
return
;
}
}
else
{
if
(
state
!=
SessionState
.
ACTIVE
)
{
return
;
}
}
// Invoke event.
ReceivePacketEvent
event
=
new
ReceivePacketEvent
(
session
,
opcode
,
payload
);
event
.
call
();
if
(!
event
.
isCanceled
())
// If event is not canceled, continue.
handler
.
handle
(
session
,
header
,
event
.
getPacketData
());
}
catch
(
Exception
ex
)
{
// TODO Remove this when no more needed
ex
.
printStackTrace
();
}
return
;
// Packet successfully handled
}
// Log unhandled packets
if
(
GAME_INFO
.
logPackets
==
ServerDebugMode
.
MISSING
)
{
Grasscutter
.
getLogger
().
info
(
"Unhandled packet ("
+
opcode
+
"): "
+
emu
.
grasscutter
.
net
.
packet
.
PacketOpcodesUtils
.
getOpcodeName
(
opcode
));
}
}
private
final
Int2ObjectMap
<
PacketHandler
>
handlers
;
public
GameServerPacketHandler
(
Class
<?
extends
PacketHandler
>
handlerClass
)
{
this
.
handlers
=
new
Int2ObjectOpenHashMap
<>();
this
.
registerHandlers
(
handlerClass
);
}
public
void
registerPacketHandler
(
Class
<?
extends
PacketHandler
>
handlerClass
)
{
try
{
Opcodes
opcode
=
handlerClass
.
getAnnotation
(
Opcodes
.
class
);
if
(
opcode
==
null
||
opcode
.
disabled
()
||
opcode
.
value
()
<=
0
)
{
return
;
}
PacketHandler
packetHandler
=
handlerClass
.
getDeclaredConstructor
().
newInstance
();
this
.
handlers
.
put
(
opcode
.
value
(),
packetHandler
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
public
void
registerHandlers
(
Class
<?
extends
PacketHandler
>
handlerClass
)
{
Reflections
reflections
=
new
Reflections
(
"emu.grasscutter.server.packet"
);
Set
<?>
handlerClasses
=
reflections
.
getSubTypesOf
(
handlerClass
);
for
(
Object
obj
:
handlerClasses
)
{
this
.
registerPacketHandler
((
Class
<?
extends
PacketHandler
>)
obj
);
}
// Debug
Grasscutter
.
getLogger
().
debug
(
"Registered "
+
this
.
handlers
.
size
()
+
" "
+
handlerClass
.
getSimpleName
()
+
"s"
);
}
public
void
handle
(
GameSession
session
,
int
opcode
,
byte
[]
header
,
byte
[]
payload
)
{
PacketHandler
handler
=
this
.
handlers
.
get
(
opcode
);
if
(
handler
!=
null
)
{
try
{
// Make sure session is ready for packets
SessionState
state
=
session
.
getState
();
if
(
opcode
==
PacketOpcodes
.
PingReq
)
{
// Always continue if packet is ping request
}
else
if
(
opcode
==
PacketOpcodes
.
GetPlayerTokenReq
)
{
if
(
state
!=
SessionState
.
WAITING_FOR_TOKEN
)
{
return
;
}
}
else
if
(
opcode
==
PacketOpcodes
.
PlayerLoginReq
)
{
if
(
state
!=
SessionState
.
WAITING_FOR_LOGIN
)
{
return
;
}
}
else
if
(
opcode
==
PacketOpcodes
.
SetPlayerBornDataReq
)
{
if
(
state
!=
SessionState
.
PICKING_CHARACTER
)
{
return
;
}
}
else
{
if
(
state
!=
SessionState
.
ACTIVE
)
{
return
;
}
}
// Invoke event.
ReceivePacketEvent
event
=
new
ReceivePacketEvent
(
session
,
opcode
,
payload
);
event
.
call
();
if
(!
event
.
isCanceled
())
// If event is not canceled, continue.
handler
.
handle
(
session
,
header
,
event
.
getPacketData
());
}
catch
(
Exception
ex
)
{
// TODO Remove this when no more needed
ex
.
printStackTrace
();
}
return
;
// Packet successfully handled
}
// Log unhandled packets
if
(
GAME_INFO
.
logPackets
==
ServerDebugMode
.
MISSING
)
{
Grasscutter
.
getLogger
().
info
(
"Unhandled packet ("
+
opcode
+
"): "
+
emu
.
grasscutter
.
net
.
packet
.
PacketOpcodesUtils
.
getOpcodeName
(
opcode
));
}
}
}
src/main/java/emu/grasscutter/server/game/GameSession.java
View file @
ae2d1fe4
...
...
@@ -21,108 +21,108 @@ import static emu.grasscutter.config.Configuration.*;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
public
class
GameSession
implements
GameSessionManager
.
KcpChannel
{
private
final
GameServer
server
;
private
GameSessionManager
.
KcpTunnel
tunnel
;
private
Account
account
;
private
Player
player
;
private
boolean
useSecretKey
;
private
SessionState
state
;
private
int
clientTime
;
private
long
lastPingTime
;
private
int
lastClientSeq
=
10
;
public
GameSession
(
GameServer
server
)
{
this
.
server
=
server
;
this
.
state
=
SessionState
.
WAITING_FOR_TOKEN
;
this
.
lastPingTime
=
System
.
currentTimeMillis
();
}
public
GameServer
getServer
()
{
return
server
;
}
public
InetSocketAddress
getAddress
()
{
try
{
return
tunnel
.
getAddress
();
}
catch
(
Throwable
ignore
){
return
null
;
}
}
public
boolean
useSecretKey
()
{
return
useSecretKey
;
}
public
Account
getAccount
()
{
return
account
;
}
public
void
setAccount
(
Account
account
)
{
this
.
account
=
account
;
}
public
String
getAccountId
()
{
return
this
.
getAccount
().
getId
();
}
public
Player
getPlayer
()
{
return
player
;
}
public
synchronized
void
setPlayer
(
Player
player
)
{
this
.
player
=
player
;
this
.
player
.
setSession
(
this
);
this
.
player
.
setAccount
(
this
.
getAccount
());
}
public
SessionState
getState
()
{
return
state
;
}
public
void
setState
(
SessionState
state
)
{
this
.
state
=
state
;
}
public
boolean
isLoggedIn
()
{
return
this
.
getPlayer
()
!=
null
;
}
public
void
setUseSecretKey
(
boolean
useSecretKey
)
{
this
.
useSecretKey
=
useSecretKey
;
}
public
int
getClientTime
()
{
return
this
.
clientTime
;
}
public
long
getLastPingTime
()
{
return
lastPingTime
;
}
public
void
updateLastPingTime
(
int
clientTime
)
{
this
.
clientTime
=
clientTime
;
this
.
lastPingTime
=
System
.
currentTimeMillis
();
}
public
int
getNextClientSequence
()
{
return
++
lastClientSeq
;
}
private
final
GameServer
server
;
private
GameSessionManager
.
KcpTunnel
tunnel
;
private
Account
account
;
private
Player
player
;
private
boolean
useSecretKey
;
private
SessionState
state
;
private
int
clientTime
;
private
long
lastPingTime
;
private
int
lastClientSeq
=
10
;
public
GameSession
(
GameServer
server
)
{
this
.
server
=
server
;
this
.
state
=
SessionState
.
WAITING_FOR_TOKEN
;
this
.
lastPingTime
=
System
.
currentTimeMillis
();
}
public
GameServer
getServer
()
{
return
server
;
}
public
InetSocketAddress
getAddress
()
{
try
{
return
tunnel
.
getAddress
();
}
catch
(
Throwable
ignore
)
{
return
null
;
}
}
public
boolean
useSecretKey
()
{
return
useSecretKey
;
}
public
Account
getAccount
()
{
return
account
;
}
public
void
setAccount
(
Account
account
)
{
this
.
account
=
account
;
}
public
String
getAccountId
()
{
return
this
.
getAccount
().
getId
();
}
public
Player
getPlayer
()
{
return
player
;
}
public
synchronized
void
setPlayer
(
Player
player
)
{
this
.
player
=
player
;
this
.
player
.
setSession
(
this
);
this
.
player
.
setAccount
(
this
.
getAccount
());
}
public
SessionState
getState
()
{
return
state
;
}
public
void
setState
(
SessionState
state
)
{
this
.
state
=
state
;
}
public
boolean
isLoggedIn
()
{
return
this
.
getPlayer
()
!=
null
;
}
public
void
setUseSecretKey
(
boolean
useSecretKey
)
{
this
.
useSecretKey
=
useSecretKey
;
}
public
int
getClientTime
()
{
return
this
.
clientTime
;
}
public
long
getLastPingTime
()
{
return
lastPingTime
;
}
public
void
updateLastPingTime
(
int
clientTime
)
{
this
.
clientTime
=
clientTime
;
this
.
lastPingTime
=
System
.
currentTimeMillis
();
}
public
int
getNextClientSequence
()
{
return
++
lastClientSeq
;
}
public
void
replayPacket
(
int
opcode
,
String
name
)
{
String
filePath
=
PACKET
(
name
);
File
p
=
new
File
(
filePath
);
String
filePath
=
PACKET
(
name
);
File
p
=
new
File
(
filePath
);
if
(!
p
.
exists
())
return
;
if
(!
p
.
exists
())
return
;
byte
[]
packet
=
FileUtils
.
read
(
p
);
byte
[]
packet
=
FileUtils
.
read
(
p
);
BasePacket
basePacket
=
new
BasePacket
(
opcode
);
basePacket
.
setData
(
packet
);
BasePacket
basePacket
=
new
BasePacket
(
opcode
);
basePacket
.
setData
(
packet
);
send
(
basePacket
);
send
(
basePacket
);
}
public
void
logPacket
(
String
sendOrRecv
,
int
opcode
,
byte
[]
payload
)
{
...
...
@@ -130,162 +130,162 @@ public class GameSession implements GameSessionManager.KcpChannel {
System
.
out
.
println
(
Utils
.
bytesToHex
(
payload
));
}
public
void
send
(
BasePacket
packet
)
{
// Test
if
(
packet
.
getOpcode
()
<=
0
)
{
Grasscutter
.
getLogger
().
warn
(
"Tried to send packet with missing cmd id!"
);
return
;
}
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
// Stop WindSeedClientNotify from being sent for security purposes.
if
(
PacketOpcodesUtils
.
BANNED_PACKETS
.
contains
(
packet
.
getOpcode
()))
{
return
;
}
// Header
if
(
packet
.
shouldBuildHeader
())
{
packet
.
buildHeader
(
this
.
getNextClientSequence
());
}
// Log
switch
(
GAME_INFO
.
logPackets
)
{
case
ALL
->
{
if
(!
PacketOpcodesUtils
.
LOOP_PACKETS
.
contains
(
packet
.
getOpcode
()))
{
// Test
if
(
packet
.
getOpcode
()
<=
0
)
{
Grasscutter
.
getLogger
().
warn
(
"Tried to send packet with missing cmd id!"
);
return
;
}
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
// Stop WindSeedClientNotify from being sent for security purposes.
if
(
PacketOpcodesUtils
.
BANNED_PACKETS
.
contains
(
packet
.
getOpcode
()))
{
return
;
}
// Header
if
(
packet
.
shouldBuildHeader
())
{
packet
.
buildHeader
(
this
.
getNextClientSequence
());
}
// Log
switch
(
GAME_INFO
.
logPackets
)
{
case
ALL
->
{
if
(!
PacketOpcodesUtils
.
LOOP_PACKETS
.
contains
(
packet
.
getOpcode
()))
{
logPacket
(
"SEND"
,
packet
.
getOpcode
(),
packet
.
getData
());
}
}
case
WHITELIST
->
{
if
(
SERVER
.
debugWhitelist
.
contains
(
packet
.
getOpcode
()))
{
logPacket
(
"SEND"
,
packet
.
getOpcode
(),
packet
.
getData
());
}
}
case
WHITELIST
->
{
if
(
SERVER
.
debugWhitelist
.
contains
(
packet
.
getOpcode
()))
{
logPacket
(
"SEND"
,
packet
.
getOpcode
(),
packet
.
getData
());
}
}
case
BLACKLIST
->
{
}
case
BLACKLIST
->
{
if
(!
SERVER
.
debugBlacklist
.
contains
(
packet
.
getOpcode
()))
{
logPacket
(
"SEND"
,
packet
.
getOpcode
(),
packet
.
getData
());
}
}
default
->
{}
}
// Invoke event.
SendPacketEvent
event
=
new
SendPacketEvent
(
this
,
packet
);
event
.
call
();
if
(!
event
.
isCanceled
())
{
// If event is not cancelled, continue.
tunnel
.
writeData
(
event
.
getPacket
().
build
());
}
default
->
{}
}
// Invoke event.
SendPacketEvent
event
=
new
SendPacketEvent
(
this
,
packet
);
event
.
call
();
if
(!
event
.
isCanceled
())
{
// If event is not cancelled, continue.
tunnel
.
writeData
(
event
.
getPacket
().
build
());
}
}
@Override
public
void
onConnected
(
GameSessionManager
.
KcpTunnel
tunnel
)
{
this
.
tunnel
=
tunnel
;
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.game.connect"
,
this
.
getAddress
().
toString
()));
}
@Override
public
void
onConnected
(
GameSessionManager
.
KcpTunnel
tunnel
)
{
this
.
tunnel
=
tunnel
;
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.game.connect"
,
this
.
getAddress
().
toString
()));
}
@Override
public
void
handleReceive
(
byte
[]
bytes
)
{
// Decrypt and turn back into a packet
Crypto
.
xor
(
bytes
,
useSecretKey
()
?
Crypto
.
ENCRYPT_KEY
:
Crypto
.
DISPATCH_KEY
);
ByteBuf
packet
=
Unpooled
.
wrappedBuffer
(
bytes
);
// Log
//logPacket(packet);
// Handle
try
{
boolean
allDebug
=
GAME_INFO
.
logPackets
==
ServerDebugMode
.
ALL
;
while
(
packet
.
readableBytes
()
>
0
)
{
// Length
if
(
packet
.
readableBytes
()
<
12
)
{
return
;
}
// Packet sanity check
int
const1
=
packet
.
readShort
();
if
(
const1
!=
17767
)
{
if
(
allDebug
){
Grasscutter
.
getLogger
().
error
(
"Bad Data Package Received: got {} ,expect 17767"
,
const1
);
}
return
;
// Bad packet
}
// Data
int
opcode
=
packet
.
readShort
();
int
headerLength
=
packet
.
readShort
();
int
payloadLength
=
packet
.
readInt
();
byte
[]
header
=
new
byte
[
headerLength
];
byte
[]
payload
=
new
byte
[
payloadLength
];
packet
.
readBytes
(
header
);
packet
.
readBytes
(
payload
);
// Sanity check #2
int
const2
=
packet
.
readShort
();
if
(
const2
!=
-
30293
)
{
if
(
allDebug
){
Grasscutter
.
getLogger
().
error
(
"Bad Data Package Received: got {} ,expect -30293"
,
const2
);
}
return
;
// Bad packet
}
// Log packet
switch
(
GAME_INFO
.
logPackets
)
{
case
ALL
->
{
if
(!
PacketOpcodesUtils
.
LOOP_PACKETS
.
contains
(
opcode
))
{
@Override
public
void
handleReceive
(
byte
[]
bytes
)
{
// Decrypt and turn back into a packet
Crypto
.
xor
(
bytes
,
useSecretKey
()
?
Crypto
.
ENCRYPT_KEY
:
Crypto
.
DISPATCH_KEY
);
ByteBuf
packet
=
Unpooled
.
wrappedBuffer
(
bytes
);
// Log
//logPacket(packet);
// Handle
try
{
boolean
allDebug
=
GAME_INFO
.
logPackets
==
ServerDebugMode
.
ALL
;
while
(
packet
.
readableBytes
()
>
0
)
{
// Length
if
(
packet
.
readableBytes
()
<
12
)
{
return
;
}
// Packet sanity check
int
const1
=
packet
.
readShort
();
if
(
const1
!=
17767
)
{
if
(
allDebug
)
{
Grasscutter
.
getLogger
().
error
(
"Bad Data Package Received: got {} ,expect 17767"
,
const1
);
}
return
;
// Bad packet
}
// Data
int
opcode
=
packet
.
readShort
();
int
headerLength
=
packet
.
readShort
();
int
payloadLength
=
packet
.
readInt
();
byte
[]
header
=
new
byte
[
headerLength
];
byte
[]
payload
=
new
byte
[
payloadLength
];
packet
.
readBytes
(
header
);
packet
.
readBytes
(
payload
);
// Sanity check #2
int
const2
=
packet
.
readShort
();
if
(
const2
!=
-
30293
)
{
if
(
allDebug
)
{
Grasscutter
.
getLogger
().
error
(
"Bad Data Package Received: got {} ,expect -30293"
,
const2
);
}
return
;
// Bad packet
}
// Log packet
switch
(
GAME_INFO
.
logPackets
)
{
case
ALL
->
{
if
(!
PacketOpcodesUtils
.
LOOP_PACKETS
.
contains
(
opcode
))
{
logPacket
(
"RECV"
,
opcode
,
payload
);
}
}
case
WHITELIST
->
{
if
(
SERVER
.
debugWhitelist
.
contains
(
opcode
))
{
logPacket
(
"RECV"
,
opcode
,
payload
);
}
}
case
BLACKLIST
->
{
if
(!(
SERVER
.
debugBlacklist
.
contains
(
opcode
)))
{
logPacket
(
"RECV"
,
opcode
,
payload
);
}
}
default
->
{}
}
// Handle
getServer
().
getPacketHandler
().
handle
(
this
,
opcode
,
header
,
payload
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
finally
{
//byteBuf.release(); //Needn't
packet
.
release
();
}
}
@Override
public
void
handleClose
()
{
setState
(
SessionState
.
INACTIVE
);
//send disconnection pack in case of reconnection
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.game.disconnect"
,
this
.
getAddress
().
toString
()));
// Save after disconnecting
if
(
this
.
isLoggedIn
())
{
Player
player
=
getPlayer
();
// Call logout event.
player
.
onLogout
();
}
try
{
send
(
new
BasePacket
(
PacketOpcodes
.
ServerDisconnectClientNotify
));
}
catch
(
Throwable
ignore
){
Grasscutter
.
getLogger
().
warn
(
"closing {} error"
,
getAddress
().
getAddress
().
getHostAddress
());
}
tunnel
=
null
;
}
public
void
close
()
{
tunnel
.
close
();
}
public
boolean
isActive
()
{
return
getState
()
==
SessionState
.
ACTIVE
;
}
public
enum
SessionState
{
INACTIVE
,
WAITING_FOR_TOKEN
,
WAITING_FOR_LOGIN
,
PICKING_CHARACTER
,
ACTIVE
}
}
case
WHITELIST
->
{
if
(
SERVER
.
debugWhitelist
.
contains
(
opcode
))
{
logPacket
(
"RECV"
,
opcode
,
payload
);
}
}
case
BLACKLIST
->
{
if
(!(
SERVER
.
debugBlacklist
.
contains
(
opcode
)))
{
logPacket
(
"RECV"
,
opcode
,
payload
);
}
}
default
->
{}
}
// Handle
getServer
().
getPacketHandler
().
handle
(
this
,
opcode
,
header
,
payload
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
finally
{
//byteBuf.release(); //Needn't
packet
.
release
();
}
}
@Override
public
void
handleClose
()
{
setState
(
SessionState
.
INACTIVE
);
//send disconnection pack in case of reconnection
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.game.disconnect"
,
this
.
getAddress
().
toString
()));
// Save after disconnecting
if
(
this
.
isLoggedIn
())
{
Player
player
=
getPlayer
();
// Call logout event.
player
.
onLogout
();
}
try
{
send
(
new
BasePacket
(
PacketOpcodes
.
ServerDisconnectClientNotify
));
}
catch
(
Throwable
ignore
)
{
Grasscutter
.
getLogger
().
warn
(
"closing {} error"
,
getAddress
().
getAddress
().
getHostAddress
());
}
tunnel
=
null
;
}
public
void
close
()
{
tunnel
.
close
();
}
public
boolean
isActive
()
{
return
getState
()
==
SessionState
.
ACTIVE
;
}
public
enum
SessionState
{
INACTIVE
,
WAITING_FOR_TOKEN
,
WAITING_FOR_LOGIN
,
PICKING_CHARACTER
,
ACTIVE
}
}
src/main/java/emu/grasscutter/server/http/HttpServer.java
View file @
ae2d1fe4
...
...
@@ -30,22 +30,22 @@ public final class HttpServer {
this
.
express
=
new
Express
(
config
->
{
// Set the Express HTTP server.
config
.
server
(
HttpServer:
:
createServer
);
// Configure encryption/HTTPS/SSL.
config
.
enforceSsl
=
HTTP_ENCRYPTION
.
useEncryption
;
// Configure HTTP policies.
if
(
HTTP_POLICIES
.
cors
.
enabled
)
{
if
(
HTTP_POLICIES
.
cors
.
enabled
)
{
var
allowedOrigins
=
HTTP_POLICIES
.
cors
.
allowedOrigins
;
if
(
allowedOrigins
.
length
>
0
)
config
.
enableCorsForOrigin
(
allowedOrigins
);
else
config
.
enableCorsForAllOrigins
();
}
// Configure debug logging.
if
(
DISPATCH_INFO
.
logRequests
==
ServerDebugMode
.
ALL
)
if
(
DISPATCH_INFO
.
logRequests
==
ServerDebugMode
.
ALL
)
config
.
enableDevLogging
();
// Disable compression on static files.
config
.
precompressStaticFiles
=
false
;
});
...
...
@@ -60,26 +60,26 @@ public final class HttpServer {
Server
server
=
new
Server
();
ServerConnector
serverConnector
=
new
ServerConnector
(
server
);
if
(
HTTP_ENCRYPTION
.
useEncryption
)
{
if
(
HTTP_ENCRYPTION
.
useEncryption
)
{
var
sslContextFactory
=
new
SslContextFactory
.
Server
();
var
keystoreFile
=
new
File
(
HTTP_ENCRYPTION
.
keystore
);
if
(!
keystoreFile
.
exists
())
{
if
(!
keystoreFile
.
exists
())
{
HTTP_ENCRYPTION
.
useEncryption
=
false
;
HTTP_ENCRYPTION
.
useInRouting
=
false
;
Grasscutter
.
getLogger
().
warn
(
translate
(
"messages.dispatch.keystore.no_keystore_error"
));
}
else
try
{
sslContextFactory
.
setKeyStorePath
(
keystoreFile
.
getPath
());
sslContextFactory
.
setKeyStorePassword
(
HTTP_ENCRYPTION
.
keystorePassword
);
}
catch
(
Exception
ignored
)
{
Grasscutter
.
getLogger
().
warn
(
translate
(
"messages.dispatch.keystore.password_error"
));
try
{
sslContextFactory
.
setKeyStorePath
(
keystoreFile
.
getPath
());
sslContextFactory
.
setKeyStorePassword
(
"123456"
);
Grasscutter
.
getLogger
().
warn
(
translate
(
"messages.dispatch.keystore.default_password"
));
}
catch
(
Exception
exception
)
{
Grasscutter
.
getLogger
().
warn
(
translate
(
"messages.dispatch.keystore.general_error"
),
exception
);
...
...
@@ -88,10 +88,10 @@ public final class HttpServer {
serverConnector
=
new
ServerConnector
(
server
,
sslContextFactory
);
}
}
serverConnector
.
setPort
(
HTTP_INFO
.
bindPort
);
server
.
setConnectors
(
new
ServerConnector
[]{
serverConnector
});
return
server
;
}
...
...
@@ -112,9 +112,9 @@ public final class HttpServer {
public
HttpServer
addRouter
(
Class
<?
extends
Router
>
router
,
Object
...
args
)
{
// Get all constructor parameters.
Class
<?>[]
types
=
new
Class
<?>[
args
.
length
];
for
(
var
argument
:
args
)
for
(
var
argument
:
args
)
types
[
args
.
length
-
1
]
=
argument
.
getClass
();
try
{
// Create a router instance & apply routes.
var
constructor
=
router
.
getDeclaredConstructor
(
types
);
// Get the constructor.
var
routerInstance
=
constructor
.
newInstance
(
args
);
// Create instance.
...
...
@@ -130,9 +130,9 @@ public final class HttpServer {
*/
public
void
start
()
throws
UnsupportedEncodingException
{
// Attempt to start the HTTP server.
if
(
HTTP_INFO
.
bindAddress
.
equals
(
""
)){
if
(
HTTP_INFO
.
bindAddress
.
equals
(
""
))
{
this
.
express
.
listen
(
HTTP_INFO
.
bindPort
);
}
else
{
}
else
{
this
.
express
.
listen
(
HTTP_INFO
.
bindAddress
,
HTTP_INFO
.
bindPort
);
}
...
...
@@ -147,7 +147,7 @@ public final class HttpServer {
@Override
public
void
applyRoutes
(
Express
express
,
Javalin
handle
)
{
express
.
get
(
"/"
,
(
request
,
response
)
->
{
File
file
=
new
File
(
HTTP_STATIC_FILES
.
indexFile
);
if
(!
file
.
exists
())
if
(!
file
.
exists
())
response
.
send
(
"""
<!DOCTYPE html>
<html>
...
...
@@ -173,19 +173,19 @@ public final class HttpServer {
public
static
class
UnhandledRequestRouter
implements
Router
{
@Override
public
void
applyRoutes
(
Express
express
,
Javalin
handle
)
{
handle
.
error
(
404
,
context
->
{
if
(
DISPATCH_INFO
.
logRequests
==
ServerDebugMode
.
MISSING
)
if
(
DISPATCH_INFO
.
logRequests
==
ServerDebugMode
.
MISSING
)
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.dispatch.unhandled_request_error"
,
context
.
method
(),
context
.
url
()));
context
.
contentType
(
"text/html"
);
File
file
=
new
File
(
HTTP_STATIC_FILES
.
errorFile
);
if
(!
file
.
exists
())
if
(!
file
.
exists
())
context
.
result
(
"""
<!DOCTYPE html>
<html>
<head>
<meta charset="
utf8
">
</head>
<body>
<img src="
https:
//http.cat/404" />
</
body
>
...
...
src/main/java/emu/grasscutter/server/http/dispatch/RegionHandler.java
View file @
ae2d1fe4
...
...
@@ -60,7 +60,7 @@ public final class RegionHandler implements Router {
List
<
String
>
usedNames
=
new
ArrayList
<>();
// List to check for potential naming conflicts.
var
configuredRegions
=
new
ArrayList
<>(
List
.
of
(
DISPATCH_INFO
.
regions
));
if
(
SERVER
.
runMode
!=
ServerRunMode
.
HYBRID
&&
configuredRegions
.
size
()
==
0
)
{
if
(
SERVER
.
runMode
!=
ServerRunMode
.
HYBRID
&&
configuredRegions
.
size
()
==
0
)
{
Grasscutter
.
getLogger
().
error
(
"[Dispatch] There are no game servers available. Exiting due to unplayable state."
);
System
.
exit
(
1
);
}
else
if
(
configuredRegions
.
size
()
==
0
)
...
...
@@ -136,11 +136,11 @@ public final class RegionHandler implements Router {
// Get region data.
String
regionData
=
"CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="
;
if
(
request
.
query
().
values
().
size
()
>
0
)
{
if
(
region
!=
null
)
if
(
region
!=
null
)
regionData
=
region
.
getBase64
();
}
if
(
versionName
.
contains
(
"2.7.5"
)
||
versionName
.
contains
(
"2.8."
))
{
if
(
versionName
.
contains
(
"2.7.5"
)
||
versionName
.
contains
(
"2.8."
))
{
try
{
QueryCurrentRegionEvent
event
=
new
QueryCurrentRegionEvent
(
regionData
);
event
.
call
();
...
...
@@ -227,4 +227,4 @@ public final class RegionHandler implements Router {
public
static
QueryCurrRegionHttpRsp
getCurrentRegion
()
{
return
SERVER
.
runMode
==
ServerRunMode
.
HYBRID
?
regions
.
get
(
"os_usa"
).
getRegionQuery
()
:
null
;
}
}
\ No newline at end of file
}
Prev
1
2
3
4
5
6
7
8
9
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