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
5a3e9bc3
Commit
5a3e9bc3
authored
May 25, 2022
by
Akka
Committed by
Melledy
May 25, 2022
Browse files
Fixed excessive memory usage of Spatial Index
parent
d95708ec
Changes
10
Hide whitespace changes
Inline
Side-by-side
build.gradle
View file @
5a3e9bc3
...
...
@@ -86,8 +86,8 @@ dependencies {
implementation
group:
'org.luaj'
,
name:
'luaj-jse'
,
version:
'3.0.1'
implementation
group:
'ch.ethz.globis.phtree'
,
name
:
'phtree'
,
version:
'2.5.0'
implementation
group:
'com.esotericsoftware'
,
name
:
'reflectasm'
,
version:
'1.11.9'
implementation
group:
'com.github.davidmoten'
,
name
:
'rtree-multi'
,
version:
'0.1'
protobuf
files
(
'proto/'
)
...
...
src/main/java/emu/grasscutter/data/ResourceLoader.java
View file @
5a3e9bc3
...
...
@@ -8,10 +8,9 @@ import java.util.Map.Entry;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
ch.ethz.globis.phtree.PhTree
;
import
ch.ethz.globis.phtree.v16.PhTree16
;
import
com.google.gson.Gson
;
import
emu.grasscutter.data.custom.*
;
import
emu.grasscutter.scripts.SceneIndexManager
;
import
emu.grasscutter.utils.Utils
;
import
lombok.SneakyThrows
;
import
org.reflections.Reflections
;
...
...
@@ -428,15 +427,12 @@ public class ResourceLoader {
continue
;
}
PhTree
<
SceneNpcBornEntry
>
index
=
new
PhTree16
<>(
3
);
var
data
=
Grasscutter
.
getGsonFactory
().
fromJson
(
Files
.
readString
(
file
),
SceneNpcBornData
.
class
);
if
(
data
.
getBornPosList
()
==
null
||
data
.
getBornPosList
().
size
()
==
0
){
continue
;
}
data
.
getBornPosList
().
forEach
(
item
->
index
.
put
(
item
.
getPos
().
toLongArray
(),
item
));
data
.
setIndex
(
index
);
data
.
setIndex
(
SceneIndexManager
.
buildIndex
(
3
,
data
.
getBornPosList
(),
item
->
item
.
getPos
().
toPoint
())
);
GameData
.
getSceneNpcBornData
().
put
(
data
.
getSceneId
(),
data
);
}
...
...
src/main/java/emu/grasscutter/data/custom/SceneNpcBornData.java
View file @
5a3e9bc3
package
emu.grasscutter.data.custom
;
import
ch.ethz.globis.phtree.PhTree
;
import
com.github.davidmoten.rtreemulti.RTree
;
import
com.github.davidmoten.rtreemulti.geometry.Geometry
;
import
emu.grasscutter.scripts.data.SceneGroup
;
import
lombok.AccessLevel
;
import
lombok.Data
;
...
...
@@ -19,7 +20,7 @@ public class SceneNpcBornData {
/**
* Spatial Index For NPC
*/
transient
Ph
Tree
<
SceneNpcBornEntry
>
index
;
transient
R
Tree
<
SceneNpcBornEntry
,
Geometry
>
index
;
/**
* npc groups
...
...
src/main/java/emu/grasscutter/game/world/Scene.java
View file @
5a3e9bc3
...
...
@@ -498,11 +498,11 @@ public class Scene {
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
toRemove
,
VisionType
.
VISION_REMOVE
));
}
}
public
Set
<
SceneBlock
>
getPlayerActiveBlocks
(
Player
player
){
// TODO consider the borders of blocks
return
getScriptManager
().
getBlocks
().
values
().
stream
()
.
filter
(
block
->
block
.
contains
(
player
.
getPos
()))
.
collect
(
Collectors
.
toSet
()
);
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
.
getPos
().
toXZDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
}
public
void
checkBlocks
()
{
Set
<
SceneBlock
>
visible
=
new
HashSet
<>();
...
...
@@ -542,9 +542,8 @@ public class Scene {
}
public
List
<
SceneGroup
>
playerMeetGroups
(
Player
player
,
SceneBlock
block
){
int
RANGE
=
100
;
List
<
SceneGroup
>
sceneGroups
=
SceneIndexManager
.
queryNeighbors
(
block
.
sceneGroupIndex
,
player
.
getPos
(),
RANGE
);
List
<
SceneGroup
>
sceneGroups
=
SceneIndexManager
.
queryNeighbors
(
block
.
sceneGroupIndex
,
player
.
getPos
().
toDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
List
<
SceneGroup
>
groups
=
sceneGroups
.
stream
()
.
filter
(
group
->
!
scriptManager
.
getLoadedGroupSetPerBlock
().
get
(
block
.
id
).
contains
(
group
))
...
...
@@ -732,14 +731,14 @@ public class Scene {
return
List
.
of
();
}
int
RANGE
=
100
;
var
pos
=
player
.
getPos
();
var
data
=
GameData
.
getSceneNpcBornData
().
get
(
getId
());
if
(
data
==
null
){
return
List
.
of
();
}
var
npcs
=
SceneIndexManager
.
queryNeighbors
(
data
.
getIndex
(),
pos
.
toLongArray
(),
RANGE
);
var
npcs
=
SceneIndexManager
.
queryNeighbors
(
data
.
getIndex
(),
pos
.
toDoubleArray
(),
Grasscutter
.
getConfig
().
server
.
game
.
loadEntitiesForPlayerRange
);
var
entityNPCS
=
npcs
.
stream
().
map
(
item
->
{
var
group
=
data
.
getGroups
().
get
(
item
.
getGroupId
());
if
(
group
==
null
){
...
...
src/main/java/emu/grasscutter/scripts/SceneIndexManager.java
View file @
5a3e9bc3
package
emu.grasscutter.scripts
;
import
ch.ethz.globis.phtree.PhTree
;
import
emu.grasscutter.utils.Position
;
import
com.github.davidmoten.rtreemulti.Entry
;
import
com.github.davidmoten.rtreemulti.RTree
;
import
com.github.davidmoten.rtreemulti.geometry.Geometry
;
import
com.github.davidmoten.rtreemulti.geometry.Rectangle
;
import
java.util.ArrayList
;
import
java.util.Collection
;
...
...
@@ -10,31 +12,20 @@ import java.util.function.Function;
public
class
SceneIndexManager
{
public
static
<
T
>
void
buildIndex
(
PhTree
<
T
>
tree
,
Collection
<
T
>
elements
,
Function
<
T
,
long
[]>
extractor
){
elements
.
forEach
(
e
->
tree
.
put
(
extractor
.
apply
(
e
),
e
));
public
static
<
T
>
RTree
<
T
,
Geometry
>
buildIndex
(
int
dimensions
,
Collection
<
T
>
elements
,
Function
<
T
,
Geometry
>
extractor
){
RTree
<
T
,
Geometry
>
rtree
=
RTree
.
dimensions
(
dimensions
).
create
();
return
rtree
.
add
(
elements
.
stream
().
map
(
e
->
Entry
.
entry
(
e
,
extractor
.
apply
(
e
))).
toList
());
}
public
static
<
T
>
List
<
T
>
queryNeighbors
(
Ph
Tree
<
T
>
tree
,
Position
position
,
int
range
){
public
static
<
T
>
List
<
T
>
queryNeighbors
(
R
Tree
<
T
,
Geometry
>
tree
,
double
[]
position
,
int
range
){
var
result
=
new
ArrayList
<
T
>();
var
arrPos
=
position
.
toLongArray
();
var
query
=
tree
.
query
(
calRange
(
arrPos
,
-
range
),
calRange
(
arrPos
,
range
));
while
(
query
.
hasNext
()){
var
element
=
query
.
next
();
result
.
add
(
element
);
}
return
result
;
}
public
static
<
T
>
List
<
T
>
queryNeighbors
(
PhTree
<
T
>
tree
,
long
[]
position
,
int
range
){
var
result
=
new
ArrayList
<
T
>();
var
query
=
tree
.
query
(
calRange
(
position
,
-
range
),
calRange
(
position
,
range
));
while
(
query
.
hasNext
()){
var
element
=
query
.
next
();
result
.
add
(
element
);
}
Rectangle
rectangle
=
Rectangle
.
create
(
calRange
(
position
,
-
range
),
calRange
(
position
,
range
));
var
queryResult
=
tree
.
search
(
rectangle
);
queryResult
.
forEach
(
q
->
result
.
add
(
q
.
value
()));
return
result
;
}
private
static
long
[]
calRange
(
long
[]
position
,
int
range
){
private
static
double
[]
calRange
(
double
[]
position
,
int
range
){
var
newPos
=
position
.
clone
();
for
(
int
i
=
0
;
i
<
position
.
length
;
i
++){
for
(
int
i
=
0
;
i
<
newPos
.
length
;
i
++){
newPos
[
i
]
+=
range
;
}
return
newPos
;
...
...
src/main/java/emu/grasscutter/scripts/SceneScriptManager.java
View file @
5a3e9bc3
package
emu.grasscutter.scripts
;
import
ch.ethz.globis.phtree.PhTree
;
import
com.github.davidmoten.rtreemulti.RTree
;
import
com.github.davidmoten.rtreemulti.geometry.Geometry
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.def.MonsterData
;
...
...
@@ -420,7 +421,7 @@ public class SceneScriptManager {
getScene
().
addEntities
(
gameEntity
);
}
public
Ph
Tree
<
SceneBlock
>
getBlocksIndex
()
{
public
R
Tree
<
SceneBlock
,
Geometry
>
getBlocksIndex
()
{
return
meta
.
sceneBlockIndex
;
}
public
void
removeMonstersInGroup
(
SceneGroup
group
,
SceneSuite
suite
)
{
...
...
src/main/java/emu/grasscutter/scripts/data/SceneBlock.java
View file @
5a3e9bc3
package
emu.grasscutter.scripts.data
;
import
ch.ethz.globis.phtree.PhTree
;
import
ch.ethz.globis.phtree.v16.PhTree16
;
import
com.github.davidmoten.rtreemulti.RTree
;
import
com.github.davidmoten.rtreemulti.geometry.Geometry
;
import
com.github.davidmoten.rtreemulti.geometry.Rectangle
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.scripts.SceneIndexManager
;
import
emu.grasscutter.scripts.ScriptLoader
;
...
...
@@ -12,7 +13,6 @@ import lombok.ToString;
import
javax.script.Bindings
;
import
javax.script.CompiledScript
;
import
javax.script.ScriptException
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.stream.Collectors
;
...
...
@@ -27,7 +27,7 @@ public class SceneBlock {
public
int
sceneId
;
public
Map
<
Integer
,
SceneGroup
>
groups
;
public
Ph
Tree
<
SceneGroup
>
sceneGroupIndex
=
new
PhTree16
<>(
3
)
;
public
R
Tree
<
SceneGroup
,
Geometry
>
sceneGroupIndex
;
private
transient
boolean
loaded
;
// Not an actual variable in the scripts either
...
...
@@ -67,11 +67,15 @@ public class SceneBlock {
.
collect
(
Collectors
.
toMap
(
x
->
x
.
id
,
y
->
y
));
groups
.
values
().
forEach
(
g
->
g
.
block_id
=
id
);
SceneIndexManager
.
buildIndex
(
this
.
sceneGroupIndex
,
groups
.
values
(),
g
->
g
.
pos
.
to
LongArray
());
this
.
sceneGroupIndex
=
SceneIndexManager
.
buildIndex
(
3
,
groups
.
values
(),
g
->
g
.
pos
.
to
Point
());
}
catch
(
ScriptException
e
)
{
Grasscutter
.
getLogger
().
error
(
"Error loading block "
+
id
+
" in scene "
+
sceneId
,
e
);
}
Grasscutter
.
getLogger
().
info
(
"scene {} block {} is loaded successfully."
,
sceneId
,
id
);
return
this
;
}
public
Rectangle
toRectangle
()
{
return
Rectangle
.
create
(
min
.
toXZDoubleArray
(),
max
.
toXZDoubleArray
());
}
}
\ No newline at end of file
src/main/java/emu/grasscutter/scripts/data/SceneMeta.java
View file @
5a3e9bc3
package
emu.grasscutter.scripts.data
;
import
c
h.ethz.globis.phtree.Ph
Tree
;
import
c
h.ethz.globis.phtree.v16.PhTree16
;
import
c
om.github.davidmoten.rtreemulti.R
Tree
;
import
c
om.github.davidmoten.rtreemulti.geometry.Geometry
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.scripts.SceneIndexManager
;
import
emu.grasscutter.scripts.ScriptLoader
;
import
lombok.Data
;
import
lombok.Setter
;
import
lombok.ToString
;
...
...
@@ -27,7 +26,7 @@ public class SceneMeta {
public
Bindings
context
;
public
Ph
Tree
<
SceneBlock
>
sceneBlockIndex
=
new
PhTree16
<>(
2
)
;
public
R
Tree
<
SceneBlock
,
Geometry
>
sceneBlockIndex
;
public
static
SceneMeta
of
(
int
sceneId
)
{
return
new
SceneMeta
().
load
(
sceneId
);
...
...
@@ -64,8 +63,8 @@ public class SceneMeta {
}
this
.
blocks
=
blocks
.
stream
().
collect
(
Collectors
.
toMap
(
b
->
b
.
id
,
b
->
b
));
SceneIndexManager
.
buildIndex
(
this
.
sceneBlockIndex
,
blocks
,
g
->
g
.
min
.
toXZLongArray
()
);
SceneIndexManager
.
buildIndex
(
this
.
sceneBlockIndex
,
blocks
,
g
->
g
.
max
.
toXZLongArray
());
this
.
sceneBlockIndex
=
SceneIndexManager
.
buildIndex
(
2
,
blocks
,
SceneBlock:
:
toRectangle
);
}
catch
(
ScriptException
e
)
{
Grasscutter
.
getLogger
().
error
(
"Error running script"
,
e
);
return
null
;
...
...
src/main/java/emu/grasscutter/utils/ConfigContainer.java
View file @
5a3e9bc3
...
...
@@ -135,7 +135,8 @@ public class ConfigContainer {
public
int
bindPort
=
22102
;
/* This is the port used in the default region. */
public
int
accessPort
=
0
;
/* Entities within a certain range will be loaded for the player */
public
int
loadEntitiesForPlayerRange
=
100
;
public
boolean
enableScriptInBigWorld
=
false
;
public
boolean
enableConsole
=
true
;
public
GameOptions
gameOptions
=
new
GameOptions
();
...
...
src/main/java/emu/grasscutter/utils/Position.java
View file @
5a3e9bc3
...
...
@@ -2,6 +2,7 @@ package emu.grasscutter.utils;
import
java.io.Serializable
;
import
com.github.davidmoten.rtreemulti.geometry.Point
;
import
dev.morphia.annotations.Entity
;
import
emu.grasscutter.net.proto.VectorOuterClass.Vector
;
...
...
@@ -155,10 +156,20 @@ public class Position implements Serializable {
.
setZ
(
this
.
getZ
())
.
build
();
}
public
long
[]
toLongArray
(){
return
new
long
[]{(
long
)
x
,
(
long
)
y
,
(
long
)
z
}
;
public
Point
toPoint
(){
return
Point
.
create
(
x
,
y
,
z
)
;
}
public
long
[]
toXZLongArray
(){
return
new
long
[]{(
long
)
x
,
(
long
)
z
};
/**
* To XYZ array for Spatial Index
*/
public
double
[]
toDoubleArray
(){
return
new
double
[]{
x
,
y
,
z
};
}
/**
* To XZ array for Spatial Index (Blocks)
*/
public
double
[]
toXZDoubleArray
(){
return
new
double
[]{
x
,
z
};
}
}
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