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
55541fa1
Commit
55541fa1
authored
Jul 20, 2022
by
Melledy
Browse files
Add a `loadReader` helper function to DataLoader
Also minor name changing on a few DataLoader methods
parent
f22b92b0
Changes
14
Show whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/data/DataLoader.java
View file @
55541fa1
...
@@ -8,7 +8,9 @@ import emu.grasscutter.utils.Utils;
...
@@ -8,7 +8,9 @@ import emu.grasscutter.utils.Utils;
import
java.io.FileInputStream
;
import
java.io.FileInputStream
;
import
java.io.FileNotFoundException
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.nio.file.Path
;
import
java.nio.file.Path
;
import
java.util.List
;
import
java.util.List
;
...
@@ -28,6 +30,24 @@ public class DataLoader {
...
@@ -28,6 +30,24 @@ public class DataLoader {
return
load
(
resourcePath
,
true
);
return
load
(
resourcePath
,
true
);
}
}
/**
* Creates an input stream reader for a data file. If the file isn't found within the /data directory then it will fallback to the default within the jar resources
*
* @param resourcePath The path to the data file to be loaded.
* @return InputStreamReader of the data file.
* @throws IOException
* @throws FileNotFoundException
* @see #load(String, boolean)
*/
public
static
InputStreamReader
loadReader
(
String
resourcePath
)
throws
IOException
,
FileNotFoundException
{
try
{
InputStream
is
=
load
(
resourcePath
,
true
);
return
new
InputStreamReader
(
is
);
}
catch
(
FileNotFoundException
exception
)
{
throw
exception
;
}
}
/**
/**
* Load a data file by its name.
* Load a data file by its name.
*
*
...
@@ -49,7 +69,7 @@ public class DataLoader {
...
@@ -49,7 +69,7 @@ public class DataLoader {
return
null
;
return
null
;
}
}
public
static
void
C
heckAllFiles
()
{
public
static
void
c
heckAllFiles
()
{
try
{
try
{
List
<
Path
>
filenames
=
FileUtils
.
getPathsFromResource
(
"/defaults/data/"
);
List
<
Path
>
filenames
=
FileUtils
.
getPathsFromResource
(
"/defaults/data/"
);
...
@@ -58,16 +78,16 @@ public class DataLoader {
...
@@ -58,16 +78,16 @@ public class DataLoader {
}
else
for
(
Path
file
:
filenames
)
{
}
else
for
(
Path
file
:
filenames
)
{
String
relativePath
=
String
.
valueOf
(
file
).
split
(
"defaults[\\\\\\/]data[\\\\\\/]"
)[
1
];
String
relativePath
=
String
.
valueOf
(
file
).
split
(
"defaults[\\\\\\/]data[\\\\\\/]"
)[
1
];
C
heckAndCopyData
(
relativePath
);
c
heckAndCopyData
(
relativePath
);
}
}
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while trying to check the data folder."
,
e
);
Grasscutter
.
getLogger
().
error
(
"An error occurred while trying to check the data folder."
,
e
);
}
}
G
enerateGachaMappings
();
g
enerateGachaMappings
();
}
}
private
static
void
C
heckAndCopyData
(
String
name
)
{
private
static
void
c
heckAndCopyData
(
String
name
)
{
String
filePath
=
Utils
.
toFilePath
(
DATA
(
name
));
String
filePath
=
Utils
.
toFilePath
(
DATA
(
name
));
if
(!
Utils
.
fileExists
(
filePath
))
{
if
(!
Utils
.
fileExists
(
filePath
))
{
...
@@ -93,7 +113,7 @@ public class DataLoader {
...
@@ -93,7 +113,7 @@ public class DataLoader {
}
}
}
}
private
static
void
G
enerateGachaMappings
()
{
private
static
void
g
enerateGachaMappings
()
{
if
(!
Utils
.
fileExists
(
GachaHandler
.
gachaMappings
))
{
if
(!
Utils
.
fileExists
(
GachaHandler
.
gachaMappings
))
{
try
{
try
{
Grasscutter
.
getLogger
().
info
(
"Creating default '"
+
GachaHandler
.
gachaMappings
+
"' data"
);
Grasscutter
.
getLogger
().
info
(
"Creating default '"
+
GachaHandler
.
gachaMappings
+
"' data"
);
...
...
src/main/java/emu/grasscutter/data/ResourceLoader.java
View file @
55541fa1
...
@@ -312,9 +312,9 @@ public class ResourceLoader {
...
@@ -312,9 +312,9 @@ public class ResourceLoader {
for
(
String
name
:
spawnDataNames
)
{
for
(
String
name
:
spawnDataNames
)
{
// Load spawn entries from file
// Load spawn entries from file
try
(
InputStream
spawnDataEntries
=
DataLoader
.
load
(
name
))
{
try
(
InputStream
Reader
reader
=
DataLoader
.
load
Reader
(
name
))
{
Type
type
=
TypeToken
.
getParameterized
(
Collection
.
class
,
SpawnGroupEntry
.
class
).
getType
();
Type
type
=
TypeToken
.
getParameterized
(
Collection
.
class
,
SpawnGroupEntry
.
class
).
getType
();
List
<
SpawnGroupEntry
>
list
=
Grasscutter
.
getGsonFactory
().
fromJson
(
new
InputStreamReader
(
spawnDataEntries
)
,
type
);
List
<
SpawnGroupEntry
>
list
=
Grasscutter
.
getGsonFactory
().
fromJson
(
reader
,
type
);
// Add spawns to group if it already exists in our spawn group map
// Add spawns to group if it already exists in our spawn group map
spawnEntryMap
.
addAll
(
list
);
spawnEntryMap
.
addAll
(
list
);
...
...
src/main/java/emu/grasscutter/game/activity/ActivityManager.java
View file @
55541fa1
...
@@ -16,6 +16,7 @@ import org.reflections.Reflections;
...
@@ -16,6 +16,7 @@ import org.reflections.Reflections;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.io.InputStreamReader
;
import
java.io.Reader
;
import
java.util.*
;
import
java.util.*
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentHashMap
;
...
@@ -44,9 +45,9 @@ public class ActivityManager extends BasePlayerManager {
...
@@ -44,9 +45,9 @@ public class ActivityManager extends BasePlayerManager {
activityWatcherTypeMap
.
put
(
typeName
.
value
(),
ConstructorAccess
.
get
(
item
));
activityWatcherTypeMap
.
put
(
typeName
.
value
(),
ConstructorAccess
.
get
(
item
));
});
});
try
(
InputStream
is
=
DataLoader
.
load
(
"ActivityConfig.json"
)
;
InputStreamReader
isr
=
new
InputStreamReader
(
is
)
)
{
try
(
Reader
reader
=
DataLoader
.
load
Reader
(
"ActivityConfig.json"
))
{
List
<
ActivityConfigItem
>
activities
=
Grasscutter
.
getGsonFactory
().
fromJson
(
List
<
ActivityConfigItem
>
activities
=
Grasscutter
.
getGsonFactory
().
fromJson
(
is
r
,
reade
r
,
TypeToken
.
getParameterized
(
List
.
class
,
ActivityConfigItem
.
class
).
getType
());
TypeToken
.
getParameterized
(
List
.
class
,
ActivityConfigItem
.
class
).
getType
());
...
...
src/main/java/emu/grasscutter/game/combine/CombineManger.java
View file @
55541fa1
...
@@ -39,7 +39,7 @@ public class CombineManger extends BaseGameSystem {
...
@@ -39,7 +39,7 @@ public class CombineManger extends BaseGameSystem {
public
static
void
initialize
()
{
public
static
void
initialize
()
{
// Read the data we need for strongbox.
// Read the data we need for strongbox.
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"ReliquaryDecompose.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"ReliquaryDecompose.json"
))
{
List
<
ReliquaryDecomposeEntry
>
decomposeEntries
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ReliquaryDecomposeEntry
.
class
).
getType
());
List
<
ReliquaryDecomposeEntry
>
decomposeEntries
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ReliquaryDecomposeEntry
.
class
).
getType
());
for
(
ReliquaryDecomposeEntry
entry
:
decomposeEntries
)
{
for
(
ReliquaryDecomposeEntry
entry
:
decomposeEntries
)
{
...
...
src/main/java/emu/grasscutter/game/drop/DropSystem.java
View file @
55541fa1
...
@@ -38,7 +38,7 @@ public class DropSystem extends BaseGameSystem {
...
@@ -38,7 +38,7 @@ public class DropSystem extends BaseGameSystem {
}
}
public
synchronized
void
load
()
{
public
synchronized
void
load
()
{
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"Drop.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"Drop.json"
))
{
getDropData
().
clear
();
getDropData
().
clear
();
List
<
DropInfo
>
banners
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
DropInfo
.
class
).
getType
());
List
<
DropInfo
>
banners
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
DropInfo
.
class
).
getType
());
if
(
banners
.
size
()
>
0
)
{
if
(
banners
.
size
()
>
0
)
{
...
...
src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java
View file @
55541fa1
...
@@ -46,7 +46,7 @@ public class DungeonChallenge extends WorldChallenge {
...
@@ -46,7 +46,7 @@ public class DungeonChallenge extends WorldChallenge {
public
static
void
initialize
()
{
public
static
void
initialize
()
{
// Read the data we need for dungeon rewards drops.
// Read the data we need for dungeon rewards drops.
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"DungeonDrop.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"DungeonDrop.json"
))
{
List
<
DungeonDrop
>
dungeonDropList
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
DungeonDrop
.
class
).
getType
());
List
<
DungeonDrop
>
dungeonDropList
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
DungeonDrop
.
class
).
getType
());
for
(
DungeonDrop
entry
:
dungeonDropList
)
{
for
(
DungeonDrop
entry
:
dungeonDropList
)
{
...
...
src/main/java/emu/grasscutter/game/expedition/ExpeditionSystem.java
View file @
55541fa1
...
@@ -30,7 +30,7 @@ public class ExpeditionSystem extends BaseGameSystem {
...
@@ -30,7 +30,7 @@ public class ExpeditionSystem extends BaseGameSystem {
}
}
public
synchronized
void
load
()
{
public
synchronized
void
load
()
{
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"ExpeditionReward.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"ExpeditionReward.json"
))
{
getExpeditionRewardDataList
().
clear
();
getExpeditionRewardDataList
().
clear
();
List
<
ExpeditionRewardInfo
>
banners
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ExpeditionRewardInfo
.
class
).
getType
());
List
<
ExpeditionRewardInfo
>
banners
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ExpeditionRewardInfo
.
class
).
getType
());
if
(
banners
.
size
()
>
0
)
{
if
(
banners
.
size
()
>
0
)
{
...
...
src/main/java/emu/grasscutter/game/gacha/GachaSystem.java
View file @
55541fa1
...
@@ -76,7 +76,7 @@ public class GachaSystem extends BaseGameSystem {
...
@@ -76,7 +76,7 @@ public class GachaSystem extends BaseGameSystem {
}
}
public
synchronized
void
load
()
{
public
synchronized
void
load
()
{
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"Banners.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"Banners.json"
))
{
getGachaBanners
().
clear
();
getGachaBanners
().
clear
();
List
<
GachaBanner
>
banners
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
GachaBanner
.
class
).
getType
());
List
<
GachaBanner
>
banners
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
GachaBanner
.
class
).
getType
());
if
(
banners
.
size
()
>
0
)
{
if
(
banners
.
size
()
>
0
)
{
...
...
src/main/java/emu/grasscutter/game/managers/AnnouncementSystem.java
View file @
55541fa1
...
@@ -30,7 +30,7 @@ public class AnnouncementSystem extends BaseGameSystem {
...
@@ -30,7 +30,7 @@ public class AnnouncementSystem extends BaseGameSystem {
}
}
private
int
loadConfig
()
{
private
int
loadConfig
()
{
try
(
var
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"Announcement.json"
))
)
{
try
(
var
fileReader
=
DataLoader
.
load
Reader
(
"Announcement.json"
))
{
List
<
AnnounceConfigItem
>
announceConfigItems
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
List
<
AnnounceConfigItem
>
announceConfigItems
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
List
.
class
,
AnnounceConfigItem
.
class
).
getType
());
TypeToken
.
getParameterized
(
List
.
class
,
AnnounceConfigItem
.
class
).
getType
());
...
...
src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java
View file @
55541fa1
...
@@ -62,7 +62,7 @@ public class EnergyManager extends BasePlayerManager {
...
@@ -62,7 +62,7 @@ public class EnergyManager extends BasePlayerManager {
public
static
void
initialize
()
{
public
static
void
initialize
()
{
// Read the data we need for monster energy drops.
// Read the data we need for monster energy drops.
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"EnergyDrop.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"EnergyDrop.json"
))
{
List
<
EnergyDropEntry
>
energyDropList
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
EnergyDropEntry
.
class
).
getType
());
List
<
EnergyDropEntry
>
energyDropList
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
EnergyDropEntry
.
class
).
getType
());
for
(
EnergyDropEntry
entry
:
energyDropList
)
{
for
(
EnergyDropEntry
entry
:
energyDropList
)
{
...
...
src/main/java/emu/grasscutter/game/shop/ShopSystem.java
View file @
55541fa1
...
@@ -60,7 +60,7 @@ public class ShopSystem extends BaseGameSystem {
...
@@ -60,7 +60,7 @@ public class ShopSystem extends BaseGameSystem {
}
}
private
void
loadShop
()
{
private
void
loadShop
()
{
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"Shop.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"Shop.json"
))
{
getShopData
().
clear
();
getShopData
().
clear
();
List
<
ShopTable
>
banners
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ShopTable
.
class
).
getType
());
List
<
ShopTable
>
banners
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ShopTable
.
class
).
getType
());
if
(
banners
.
size
()
>
0
)
{
if
(
banners
.
size
()
>
0
)
{
...
@@ -104,7 +104,7 @@ public class ShopSystem extends BaseGameSystem {
...
@@ -104,7 +104,7 @@ public class ShopSystem extends BaseGameSystem {
}
}
private
void
loadShopChest
()
{
private
void
loadShopChest
()
{
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"ShopChest.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"ShopChest.json"
))
{
getShopChestData
().
clear
();
getShopChestData
().
clear
();
List
<
ShopChestTable
>
shopChestTableList
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ShopChestTable
.
class
).
getType
());
List
<
ShopChestTable
>
shopChestTableList
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ShopChestTable
.
class
).
getType
());
if
(
shopChestTableList
.
size
()
>
0
)
{
if
(
shopChestTableList
.
size
()
>
0
)
{
...
@@ -119,7 +119,7 @@ public class ShopSystem extends BaseGameSystem {
...
@@ -119,7 +119,7 @@ public class ShopSystem extends BaseGameSystem {
}
}
private
void
loadShopChestBatchUse
()
{
private
void
loadShopChestBatchUse
()
{
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"ShopChestBatchUse.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"ShopChestBatchUse.json"
))
{
getShopChestBatchUseData
().
clear
();
getShopChestBatchUseData
().
clear
();
List
<
ShopChestBatchUseTable
>
shopChestBatchUseTableList
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ShopChestBatchUseTable
.
class
).
getType
());
List
<
ShopChestBatchUseTable
>
shopChestBatchUseTableList
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TypeToken
.
getParameterized
(
Collection
.
class
,
ShopChestBatchUseTable
.
class
).
getType
());
if
(
shopChestBatchUseTableList
.
size
()
>
0
)
{
if
(
shopChestBatchUseTableList
.
size
()
>
0
)
{
...
...
src/main/java/emu/grasscutter/game/tower/TowerSystem.java
View file @
55541fa1
...
@@ -25,7 +25,7 @@ public class TowerSystem extends BaseGameSystem {
...
@@ -25,7 +25,7 @@ public class TowerSystem extends BaseGameSystem {
private
TowerScheduleConfig
towerScheduleConfig
;
private
TowerScheduleConfig
towerScheduleConfig
;
public
synchronized
void
load
(){
public
synchronized
void
load
(){
try
(
Reader
fileReader
=
new
InputStreamReader
(
DataLoader
.
load
(
"TowerSchedule.json"
))
)
{
try
(
Reader
fileReader
=
DataLoader
.
load
Reader
(
"TowerSchedule.json"
))
{
towerScheduleConfig
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TowerScheduleConfig
.
class
);
towerScheduleConfig
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
TowerScheduleConfig
.
class
);
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"Unable to load tower schedule config."
,
e
);
Grasscutter
.
getLogger
().
error
(
"Unable to load tower schedule config."
,
e
);
...
...
src/main/java/emu/grasscutter/game/world/WorldDataSystem.java
View file @
55541fa1
...
@@ -19,6 +19,7 @@ import emu.grasscutter.server.game.GameServer;
...
@@ -19,6 +19,7 @@ import emu.grasscutter.server.game.GameServer;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.io.InputStreamReader
;
import
java.io.Reader
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -41,9 +42,9 @@ public class WorldDataSystem extends BaseGameSystem {
...
@@ -41,9 +42,9 @@ public class WorldDataSystem extends BaseGameSystem {
// set the special chest first
// set the special chest first
chestInteractHandlerMap
.
put
(
"SceneObj_Chest_Flora"
,
new
BossChestInteractHandler
());
chestInteractHandlerMap
.
put
(
"SceneObj_Chest_Flora"
,
new
BossChestInteractHandler
());
try
(
InputStream
is
=
DataLoader
.
load
(
"ChestReward.json"
)
;
InputStreamReader
isr
=
new
InputStreamReader
(
is
)
)
{
try
(
Reader
reader
=
DataLoader
.
load
Reader
(
"ChestReward.json"
))
{
List
<
ChestReward
>
chestReward
=
Grasscutter
.
getGsonFactory
().
fromJson
(
List
<
ChestReward
>
chestReward
=
Grasscutter
.
getGsonFactory
().
fromJson
(
is
r
,
reade
r
,
TypeToken
.
getParameterized
(
List
.
class
,
ChestReward
.
class
).
getType
());
TypeToken
.
getParameterized
(
List
.
class
,
ChestReward
.
class
).
getType
());
chestReward
.
forEach
(
reward
->
chestReward
.
forEach
(
reward
->
...
...
src/main/java/emu/grasscutter/utils/Utils.java
View file @
55541fa1
...
@@ -191,7 +191,7 @@ public final class Utils {
...
@@ -191,7 +191,7 @@ public final class Utils {
createFolder
(
dataFolder
);
createFolder
(
dataFolder
);
// Make sure the data folder is populated, if there are any missing files copy them from resources
// Make sure the data folder is populated, if there are any missing files copy them from resources
DataLoader
.
C
heckAllFiles
();
DataLoader
.
c
heckAllFiles
();
if
(
exit
)
System
.
exit
(
1
);
if
(
exit
)
System
.
exit
(
1
);
}
}
...
...
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