mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-23 21:47:39 +00:00
Merge remote-tracking branch 'origin/development' into development
This commit is contained in:
commit
e60cf79309
@ -110,8 +110,7 @@ There is a dummy user named "Server" in every player's friends list that you can
|
||||
| broadcast | broadcast \<message> | server.broadcast | Both side | Sends a message to all the players. | b |
|
||||
| coop | coop \<playerId> \<target playerId> | server.coop | Both side | Forces someone to join the world of others. | |
|
||||
| changescene | changescene \<scene id> | player.changescene | Client only | Switch scenes by scene ID. | scene |
|
||||
| clearartifacts | clearartifacts | player.clearartifacts | Client only | Deletes all unequipped and unlocked level 0 artifacts, including 5-star rarity ones from your inventory. | clearart |
|
||||
| clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including 5-star rarity ones from your inventory. | clearwpns |
|
||||
| clear | clear <all\|wp\|art\|mat> [UID] | player.clearinv | Client only | Deletes all unequipped and unlocked level 0 artifacts(art)/weapons(wp)/material(all) or all, including 5-star rarity ones from your inventory. | clear |
|
||||
| drop | drop <itemID\|itemName> [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` |
|
||||
| give | give [player] <itemId\|itemName> [amount] [level] [finement] | player.give | Both side | Gives item(s) to you or the specified player. (finement option only weapon.) | `g` `item` `giveitem` |
|
||||
| givechar | givechar \<uid> \<avatarId> | player.givechar | Both side | Gives the player a specified character. | givec |
|
||||
|
@ -111,8 +111,7 @@ chmod +x gradlew
|
||||
| broadcast | broadcast <消息内容> | server.broadcast | 均可使用 | 给所有玩家发送公告 | b |
|
||||
| coop | coop \<uid> <目标uid> | server.coop | 均可使用 | 强制某位玩家进入指定玩家的多人世界 | |
|
||||
| changescene | changescene <场景ID> | player.changescene | 仅客户端 | 切换到指定场景 | scene |
|
||||
| clearartifacts | clearartifacts | player.clearartifacts | 仅客户端 | 删除所有未装备及未解锁的圣遗物,包括五星 | clearart |
|
||||
| clearweapons | clearweapons | player.clearweapons | 仅客户端 | 删除所有未装备及未解锁的武器,包括五星 | clearwp |
|
||||
| clear | clear <all\|wp\|art\|mat> [UID] | player.clearinv | 仅客户端 | 删除所有未装备及未解锁的圣遗物(art)或武器(wp)或材料(mat)或者所有(all),包括五星 | clear |
|
||||
| drop | drop <物品ID\|物品名称> [数量] | server.drop | 仅客户端 | 在指定玩家周围掉落指定物品 | `d` `dropitem` |
|
||||
| give | give [uid] <物品ID\|物品名称> [数量] [等级] [精炼等级] | | | 给予指定玩家一定数量及等级的物品 (精炼等级仅适用于武器) | `g` `item` `giveitem` |
|
||||
| givechar | givechar \<uid> <角色ID> [等级] | player.givechar | 均可使用 | 给予指定玩家对应角色 | givec |
|
||||
|
176
data/gacha_records.html
Normal file
176
data/gacha_records.html
Normal file
@ -0,0 +1,176 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
// Debug entry
|
||||
// record = [
|
||||
// {"time": 10000341, "item": 1001},
|
||||
// {"time": 10000342, "item": 1002},
|
||||
// {"time": 10000343, "item": 1003},
|
||||
// ];
|
||||
// maxPage = 5;
|
||||
|
||||
// in production environment
|
||||
record = {{REPLACE_RECORD}};
|
||||
maxPage = {{REPLACE_MAXPAGE}};
|
||||
|
||||
// TODO: implement this mapper by yourself
|
||||
// I don't want to put real items' name here to avoid being DMCA'd
|
||||
mappings = {
|
||||
'en-us': {
|
||||
200: "Standard",
|
||||
301: "Event Avatar",
|
||||
302: "Event Weapon",
|
||||
1041 : ["M0n4", "yellow"],
|
||||
1032 : ["B4nn477", "purple"],
|
||||
1035 : ["77", "yellow"]
|
||||
},
|
||||
'zh-cn': {
|
||||
// encoding issues here, maybe we should consider load mappings remotely
|
||||
// will display as "锟斤铐锟斤铐锟斤铐", lmao
|
||||
// 200: "常驻",
|
||||
// 301: "角色UP-1",
|
||||
// 302: "武器UP"
|
||||
200: "Standard",
|
||||
301: "Event Avatar",
|
||||
302: "Event Weapon",
|
||||
}
|
||||
};
|
||||
mappings['default'] = mappings['en-us'];
|
||||
</script>
|
||||
<!-- TODO: Refine the CSS -->
|
||||
<style>
|
||||
a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.content {
|
||||
width: 400px;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content .navbar {
|
||||
margin: auto;
|
||||
width: fit-content;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.yellow {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: rgb(75, 107, 251);
|
||||
}
|
||||
|
||||
.purple {
|
||||
color: rgb(242, 40, 242);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="background: skyblue;">
|
||||
<div class="content">
|
||||
<h1>Gacha Records</h1>
|
||||
<h2 id="gacha-type"></h2>
|
||||
<br/>
|
||||
<div style="width: fit-content">
|
||||
<table border="1">
|
||||
<tbody id="container">
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Item</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="navbar">
|
||||
<a href="" id="prev"> < </a>
|
||||
<span id="curpage">1</span>
|
||||
<a href="" id="next"> > </a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var lang = new window.URLSearchParams(window.location.search).get("lang");
|
||||
function itemMapper(itemID) {
|
||||
if (mappings[lang] != null && mappings[lang][itemID] != null) {
|
||||
var entry = mappings[lang][itemID];
|
||||
if (entry){
|
||||
return "<span class='" + entry[1] + "'>" + entry[0] + "</span>";
|
||||
}
|
||||
} else {
|
||||
if (mappings['default'][itemID] != null) {
|
||||
var entry = mappings['default'][itemID];
|
||||
if (entry){
|
||||
return "<span class='" + entry[1] + "'>" + entry[0] + "</span>";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "<span class='blue'>" + itemID + "</span>";
|
||||
}
|
||||
function dateFormatter(timeStamp) {
|
||||
var date = new Date(timeStamp);
|
||||
if (lang == "en-us" || lang == null) { // MM/DD/YYYY hh:mm:ss.SSS
|
||||
return String(date.getMonth()+1).padStart(2, "0") +
|
||||
"/"+String(date.getDate()).padStart(2, "0")+
|
||||
"/"+date.getFullYear()+
|
||||
" "+String(date.getHours()).padStart(2, "0")+
|
||||
":"+String(date.getMinutes()).padStart(2, "0")+
|
||||
":"+String(date.getSeconds()).padStart(2, "0")+
|
||||
"."+date.getMilliseconds();
|
||||
} else if (lang == "zh-cn") { // YYYY/MM/DD hh:mm:ss.SSS
|
||||
return date.getFullYear()+
|
||||
"/" + String(date.getMonth()+1).padStart(2, "0") +
|
||||
"/"+String(date.getDate()).padStart(2, "0")+
|
||||
" "+String(date.getHours()).padStart(2, "0")+
|
||||
":"+String(date.getMinutes()).padStart(2, "0")+
|
||||
":"+String(date.getSeconds()).padStart(2, "0")+
|
||||
"."+date.getMilliseconds();
|
||||
}
|
||||
}
|
||||
(function (){
|
||||
var container = document.getElementById("container");
|
||||
record.forEach(element => {
|
||||
var e = document.createElement("tr");
|
||||
|
||||
e.innerHTML= "<td>" + dateFormatter(element.time) + "</td><td>" + itemMapper(element.item) + "</td>";
|
||||
container.appendChild(e);
|
||||
});
|
||||
// setup pagenation buttons
|
||||
var page = parseInt(new window.URLSearchParams(window.location.search).get("p"));
|
||||
if (!page){
|
||||
page = 0;
|
||||
}
|
||||
document.getElementById("curpage").innerText = page + 1;
|
||||
var href = new URL(window.location);
|
||||
href.searchParams.set("p", page - 1);
|
||||
document.getElementById("prev").href = href.toString();
|
||||
href.searchParams.set("p", page + 1);
|
||||
document.getElementById("next").href = href.toString();
|
||||
|
||||
if (page <= 0) {
|
||||
document.getElementById("prev").style.display = "none";
|
||||
}
|
||||
if (page >= maxPage - 1) {
|
||||
document.getElementById("next").style.display = "none";
|
||||
}
|
||||
|
||||
// setup gacha type info
|
||||
var gachaType = new window.URLSearchParams(window.location.search).get("gachaType");
|
||||
var gachaString;
|
||||
if (mappings[lang] != null && mappings[lang][gachaType] != null) {
|
||||
gachaString = mappings[lang][gachaType];
|
||||
}else{
|
||||
gachaString = mappings['default'][gachaType];
|
||||
if (gachaString == null) {
|
||||
gachaString = gachaType;
|
||||
}
|
||||
}
|
||||
document.getElementById("gacha-type").innerText = gachaString;
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -3,6 +3,5 @@ syntax = "proto3";
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
message McoinExchangeHcoinRsp {
|
||||
uint32 mCoinNum = 1;
|
||||
uint32 hCoinNum = 2;
|
||||
int32 retcode = 1;
|
||||
}
|
||||
|
@ -73,6 +73,12 @@ public final class GiveCharCommand implements CommandHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check level.
|
||||
if (level > 90) {
|
||||
CommandHandler.sendMessage(sender, "Invalid avatar level.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate ascension level.
|
||||
if (level <= 40) {
|
||||
ascension = (int) Math.ceil(level / 20f);
|
||||
@ -88,6 +94,6 @@ public final class GiveCharCommand implements CommandHandler {
|
||||
avatar.recalcStats();
|
||||
|
||||
targetPlayer.addAvatar(avatar);
|
||||
CommandHandler.sendMessage(sender, String.format("Given %s to %s.", avatarId, target));
|
||||
CommandHandler.sendMessage(sender, String.format("Given %s with level %s to %s.", avatarId, level, target));
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,21 @@ public final class SetStatsCommand implements CommandHandler {
|
||||
String stat = args.get(0);
|
||||
switch (stat) {
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, "Usage: /setstats|stats <hp | def | atk | em | er | crate | cdmg> <value> for basic stats");
|
||||
CommandHandler.sendMessage(sender, "Usage: /setstats|stats <hp | mhp | def | atk | em | er | crate | cdmg> <value> for basic stats");
|
||||
CommandHandler.sendMessage(sender, "Usage: /stats <epyro | ecryo | ehydro | egeo | edend | eelec | ephys> <amount> for elemental bonus");
|
||||
return;
|
||||
case "mhp":
|
||||
try {
|
||||
int health = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, health);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_MAX_HP));
|
||||
CommandHandler.sendMessage(sender, "MAX HP set to " + health + ".");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Max HP value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "hp":
|
||||
try {
|
||||
int health = Integer.parseInt(args.get(1));
|
||||
|
@ -53,7 +53,7 @@ public final class TalentCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(sender, "To get talent ID: /talent getid");
|
||||
return;
|
||||
}
|
||||
if (nextLevel > 16){
|
||||
if (nextLevel >= 16){
|
||||
CommandHandler.sendMessage(sender, "Invalid talent level. Level should be lower than 16");
|
||||
return;
|
||||
}
|
||||
@ -117,7 +117,7 @@ public final class TalentCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(sender, "To set talent level: /talent <n or e or q> <value>");
|
||||
return;
|
||||
}
|
||||
if (nextLevel > 16){
|
||||
if (nextLevel >= 16){
|
||||
CommandHandler.sendMessage(sender, "Invalid talent level. Level should be lower than 16");
|
||||
return;
|
||||
}
|
||||
|
@ -3,11 +3,14 @@ package emu.grasscutter.database;
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import dev.morphia.query.FindOptions;
|
||||
import dev.morphia.query.Sort;
|
||||
import dev.morphia.query.experimental.filters.Filters;
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.friends.Friendship;
|
||||
import emu.grasscutter.game.gacha.GachaRecord;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
@ -78,6 +81,11 @@ public final class DatabaseHelper {
|
||||
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("token", token)).first();
|
||||
}
|
||||
|
||||
public static Account getAccountBySessionKey(String sessionKey) {
|
||||
if(sessionKey == null) return null;
|
||||
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first();
|
||||
}
|
||||
|
||||
public static Account getAccountById(String uid) {
|
||||
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first();
|
||||
}
|
||||
@ -181,5 +189,36 @@ public final class DatabaseHelper {
|
||||
)).first();
|
||||
}
|
||||
|
||||
public static List<GachaRecord> getGachaRecords(int ownerId, int page, int gachaType){
|
||||
return getGachaRecords(ownerId, page, gachaType, 10);
|
||||
}
|
||||
|
||||
public static List<GachaRecord> getGachaRecords(int ownerId, int page, int gachaType, int pageSize){
|
||||
return DatabaseManager.getDatastore().find(GachaRecord.class).filter(
|
||||
Filters.eq("ownerId", ownerId),
|
||||
Filters.eq("gachaType", gachaType)
|
||||
).iterator(new FindOptions()
|
||||
.sort(Sort.descending("transactionDate"))
|
||||
.skip(pageSize * page)
|
||||
.limit(pageSize)
|
||||
).toList();
|
||||
}
|
||||
|
||||
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType){
|
||||
return getGachaRecordsMaxPage(ownerId, page, gachaType, 10);
|
||||
}
|
||||
|
||||
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize){
|
||||
long count = DatabaseManager.getDatastore().find(GachaRecord.class).filter(
|
||||
Filters.eq("ownerId", ownerId),
|
||||
Filters.eq("gachaType", gachaType)
|
||||
).count();
|
||||
return count / 10 + (count % 10 > 0 ? 1 : 0 );
|
||||
}
|
||||
|
||||
public static void saveGachaRecord(GachaRecord gachaRecord){
|
||||
DatabaseManager.getDatastore().save(gachaRecord);
|
||||
}
|
||||
|
||||
public static char AWJVN = 'e';
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.friends.Friendship;
|
||||
import emu.grasscutter.game.gacha.GachaRecord;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
@ -28,7 +29,7 @@ public final class DatabaseManager {
|
||||
private static Datastore dispatchDatastore;
|
||||
|
||||
private static final Class<?>[] mappedClasses = new Class<?>[] {
|
||||
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class
|
||||
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, GachaRecord.class
|
||||
};
|
||||
|
||||
public static Datastore getDatastore() {
|
||||
|
@ -74,7 +74,11 @@ public class Account {
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
if(email != null && !email.isEmpty()) {
|
||||
return email;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
|
@ -91,9 +91,21 @@ public class GachaBanner {
|
||||
return eventChance;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public GachaInfo toProto() {
|
||||
String record = "http://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + "/gacha";
|
||||
|
||||
return toProto("");
|
||||
}
|
||||
public GachaInfo toProto(String sessionKey) {
|
||||
String record = "https://"
|
||||
+ (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ?
|
||||
Grasscutter.getConfig().getDispatchOptions().Ip :
|
||||
Grasscutter.getConfig().getDispatchOptions().PublicIp)
|
||||
+ ":"
|
||||
+ Integer.toString(Grasscutter.getConfig().getDispatchOptions().PublicPort == 0 ?
|
||||
Grasscutter.getConfig().getDispatchOptions().Port :
|
||||
Grasscutter.getConfig().getDispatchOptions().PublicPort)
|
||||
+ "/gacha?s=" + sessionKey + "&gachaType=" + gachaType;
|
||||
// Grasscutter.getLogger().info("record = " + record);
|
||||
GachaInfo.Builder info = GachaInfo.newBuilder()
|
||||
.setGachaType(this.getGachaType())
|
||||
.setScheduleId(this.getScheduleId())
|
||||
|
@ -14,6 +14,7 @@ import com.sun.nio.file.SensitivityWatchEventModifier;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.gacha.GachaBanner.BannerType;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
@ -196,6 +197,10 @@ public class GachaManager {
|
||||
if (itemData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Write gacha record
|
||||
GachaRecord gachaRecord = new GachaRecord(itemId, player.getUid(), gachaType);
|
||||
DatabaseHelper.saveGachaRecord(gachaRecord);
|
||||
|
||||
// Create gacha item
|
||||
GachaItem.Builder gachaItem = GachaItem.newBuilder();
|
||||
@ -321,6 +326,7 @@ public class GachaManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private synchronized GetGachaInfoRsp createProto() {
|
||||
GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345);
|
||||
|
||||
@ -330,12 +336,26 @@ public class GachaManager {
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
|
||||
private synchronized GetGachaInfoRsp createProto(String sessionKey) {
|
||||
GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345);
|
||||
|
||||
for (GachaBanner banner : getGachaBanners().values()) {
|
||||
proto.addGachaInfoList(banner.toProto(sessionKey));
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public GetGachaInfoRsp toProto() {
|
||||
if (this.cachedProto == null) {
|
||||
this.cachedProto = createProto();
|
||||
}
|
||||
|
||||
return this.cachedProto;
|
||||
}
|
||||
|
||||
public GetGachaInfoRsp toProto(String sessionKey) {
|
||||
return createProto(sessionKey);
|
||||
}
|
||||
}
|
||||
|
75
src/main/java/emu/grasscutter/game/gacha/GachaRecord.java
Normal file
75
src/main/java/emu/grasscutter/game/gacha/GachaRecord.java
Normal file
@ -0,0 +1,75 @@
|
||||
package emu.grasscutter.game.gacha;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import dev.morphia.annotations.*;
|
||||
|
||||
@Entity(value = "gachas", useDiscriminator = false)
|
||||
public class GachaRecord {
|
||||
@Id private ObjectId id;
|
||||
|
||||
@Indexed private int ownerId;
|
||||
|
||||
private Date transactionDate;
|
||||
private int itemID;
|
||||
@Indexed private int gachaType;
|
||||
|
||||
public GachaRecord() {}
|
||||
|
||||
public GachaRecord(int itemId ,int ownerId, int gachaType){
|
||||
this.transactionDate = new Date();
|
||||
this.itemID = itemId;
|
||||
this.ownerId = ownerId;
|
||||
this.gachaType = gachaType;
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public void setOwnerId(int ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
public int getGachaType() {
|
||||
return gachaType;
|
||||
}
|
||||
|
||||
public void setGachaType(int type) {
|
||||
this.gachaType = type;
|
||||
}
|
||||
|
||||
public Date getTransactionDate() {
|
||||
return transactionDate;
|
||||
}
|
||||
|
||||
public void setTransactionDate(Date transactionDate) {
|
||||
this.transactionDate = transactionDate;
|
||||
}
|
||||
|
||||
public int getItemID() {
|
||||
return itemID;
|
||||
}
|
||||
|
||||
public void setItemID(int itemID) {
|
||||
this.itemID = itemID;
|
||||
}
|
||||
|
||||
public ObjectId getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(ObjectId id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toJsonString();
|
||||
}
|
||||
public String toJsonString() {
|
||||
return "{\"time\": " + this.transactionDate.getTime() + ",\"item\":" + this.itemID + "}";
|
||||
}
|
||||
|
||||
}
|
@ -32,7 +32,7 @@ public final class DispatchHttpJsonHandler implements HttpContextHandler {
|
||||
public void handle(Request req, Response res) throws IOException {
|
||||
// Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled
|
||||
if(Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> x == req.baseUrl())) {
|
||||
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s %s request: ", req.ip(), req.method(), req.baseUrl()) + (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING ? "(MISSING)" : ""));
|
||||
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s %s request: %s", req.ip(), req.method(), req.baseUrl()) + (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING ? "(MISSING)" : ""));
|
||||
}
|
||||
res.send(response);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import emu.grasscutter.server.dispatch.json.*;
|
||||
import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData;
|
||||
import emu.grasscutter.server.event.dispatch.QueryAllRegionsEvent;
|
||||
import emu.grasscutter.server.event.dispatch.QueryCurrentRegionEvent;
|
||||
import emu.grasscutter.server.http.gacha.GachaRecordHandler;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import express.Express;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
@ -319,9 +320,6 @@ public final class DispatchServer {
|
||||
responseData.data.account.uid = account.getId();
|
||||
responseData.data.account.token = account.generateSessionKey();
|
||||
responseData.data.account.email = account.getEmail();
|
||||
if (responseData.data.account.email == null) {
|
||||
responseData.data.account.email = "";
|
||||
}
|
||||
|
||||
Grasscutter.getLogger()
|
||||
.info(String.format("[Dispatch] Client %s failed to log in: Account %s created",
|
||||
@ -346,9 +344,6 @@ public final class DispatchServer {
|
||||
responseData.data.account.uid = account.getId();
|
||||
responseData.data.account.token = account.generateSessionKey();
|
||||
responseData.data.account.email = account.getEmail();
|
||||
if (responseData.data.account.email == null) {
|
||||
responseData.data.account.email = "";
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", req.ip(),
|
||||
responseData.data.account.uid));
|
||||
@ -389,9 +384,6 @@ public final class DispatchServer {
|
||||
responseData.data.account.uid = requestData.uid;
|
||||
responseData.data.account.token = requestData.token;
|
||||
responseData.data.account.email = account.getEmail();
|
||||
if (responseData.data.account.email == null) { // null will trigger crash in some client
|
||||
responseData.data.account.email = "";
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s",
|
||||
req.ip(), responseData.data.account.uid));
|
||||
@ -494,7 +486,7 @@ public final class DispatchServer {
|
||||
// webstatic-sea.hoyoverse.com
|
||||
httpServer.get("/admin/mi18n/plat_oversea/m202003048/m202003048-version.json", new DispatchHttpJsonHandler("{\"version\":51}"));
|
||||
|
||||
httpServer.get("/gacha", (req, res) -> res.send("<!doctype html><html lang=\"en\"><head><title>Gacha</title></head><body></body></html>"));
|
||||
httpServer.get("/gacha", new GachaRecordHandler());
|
||||
|
||||
httpServer.listen(Grasscutter.getConfig().getDispatchOptions().Port);
|
||||
Grasscutter.getLogger().info("[Dispatch] Dispatch server started on port " + httpServer.raw().port());
|
||||
|
@ -16,7 +16,7 @@ public class LoginResultJson {
|
||||
public static class VerifyAccountData {
|
||||
public String uid;
|
||||
public String name = "";
|
||||
public String email;
|
||||
public String email = "";
|
||||
public String mobile = "";
|
||||
public String is_email_verify = "0";
|
||||
public String realname = "";
|
||||
|
@ -22,27 +22,29 @@ public class GameServerPacketHandler {
|
||||
|
||||
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 = (PacketHandler) handlerClass.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) {
|
||||
Class<?> c = (Class<?>) obj;
|
||||
|
||||
try {
|
||||
Opcodes opcode = c.getAnnotation(Opcodes.class);
|
||||
|
||||
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PacketHandler packetHandler = (PacketHandler) c.newInstance();
|
||||
|
||||
this.handlers.put(opcode.value(), packetHandler);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
@ -0,0 +1,52 @@
|
||||
package emu.grasscutter.server.http.gacha;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import express.http.HttpContextHandler;
|
||||
import express.http.Request;
|
||||
import express.http.Response;
|
||||
|
||||
public final class GachaRecordHandler implements HttpContextHandler {
|
||||
String render_template;
|
||||
public GachaRecordHandler() {
|
||||
File template = new File(Grasscutter.getConfig().DATA_FOLDER + "gacha_records.html");
|
||||
if (template.exists()) {
|
||||
// Load from cache
|
||||
render_template = new String(FileUtils.read(template));
|
||||
} else {
|
||||
render_template = "{{REPLACE_RECORD}}";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Request req, Response res) throws IOException {
|
||||
// Grasscutter.getLogger().info( req.query().toString() );
|
||||
String sessionKey = req.query("s");
|
||||
int page = 0;
|
||||
int gachaType = 0;
|
||||
if (req.query("p") != null) {
|
||||
page = Integer.valueOf(req.query("p"));
|
||||
}
|
||||
|
||||
if (req.query("gachaType") != null) {
|
||||
gachaType = Integer.valueOf(req.query("gachaType"));
|
||||
}
|
||||
|
||||
Account account = DatabaseHelper.getAccountBySessionKey(sessionKey);
|
||||
if (account != null) {
|
||||
String records = DatabaseHelper.getGachaRecords(account.getPlayerUid(), page, gachaType).toString();
|
||||
// Grasscutter.getLogger().info(records);
|
||||
String response = render_template.replace("{{REPLACE_RECORD}}", records)
|
||||
.replace("{{REPLACE_MAXPAGE}}", String.valueOf(DatabaseHelper.getGachaRecordsMaxPage(account.getPlayerUid(), page, gachaType)));
|
||||
|
||||
res.send(response);
|
||||
} else {
|
||||
res.send("404");
|
||||
}
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ public class HandlerBuyGoodsReq extends PacketHandler {
|
||||
session.getPlayer().save();
|
||||
}
|
||||
|
||||
if (bought + buyGoodsReq.getBoughtNum() > sg.getBuyLimit()) {
|
||||
if ((bought + buyGoodsReq.getBoughtNum() > sg.getBuyLimit()) && sg.getBuyLimit() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,10 @@ public class HandlerGetGachaInfoReq extends PacketHandler {
|
||||
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
session.send(new PacketGetGachaInfoRsp(session.getServer().getGachaManager()));
|
||||
session.send(new PacketGetGachaInfoRsp(session.getServer().getGachaManager(),
|
||||
// TODO: use other Nonce/key insteadof session key to ensure the overall security for the player
|
||||
session.getPlayer().getAccount().getSessionKey())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import emu.grasscutter.net.packet.Opcodes;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.McoinExchangeHcoinReqOuterClass;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.PacketMcoinExchangeHcoinRsp;
|
||||
|
||||
@ -15,13 +16,13 @@ public class HandlerMcoinExchangeHcoinReq extends PacketHandler {
|
||||
McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq exchangeReq = McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq.parseFrom(payload);
|
||||
|
||||
if (session.getPlayer().getCrystals() < exchangeReq.getMCoinNum() && exchangeReq.getMCoinNum() == exchangeReq.getHCoinNum()) {
|
||||
session.send(new PacketMcoinExchangeHcoinRsp(RetcodeOuterClass.Retcode.RET_UNKNOWN_ERROR_VALUE));
|
||||
return;
|
||||
}
|
||||
|
||||
session.getPlayer().setCrystals(session.getPlayer().getCrystals() - exchangeReq.getMCoinNum());
|
||||
session.getPlayer().setPrimogems(session.getPlayer().getPrimogems() + exchangeReq.getHCoinNum());
|
||||
session.getPlayer().save();
|
||||
|
||||
session.send(new PacketMcoinExchangeHcoinRsp(session.getPlayer().getCrystals(), session.getPlayer().getPrimogems()));
|
||||
session.send(new PacketMcoinExchangeHcoinRsp(RetcodeOuterClass.Retcode.RET_SUCC_VALUE));
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,17 @@ import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
|
||||
public class PacketGetGachaInfoRsp extends BasePacket {
|
||||
|
||||
@Deprecated
|
||||
public PacketGetGachaInfoRsp(GachaManager manager) {
|
||||
super(PacketOpcodes.GetGachaInfoRsp);
|
||||
|
||||
this.setData(manager.toProto());
|
||||
}
|
||||
|
||||
public PacketGetGachaInfoRsp(GachaManager manager, String sessionKey) {
|
||||
super(PacketOpcodes.GetGachaInfoRsp);
|
||||
|
||||
this.setData(manager.toProto(sessionKey));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,13 +4,19 @@ import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.GetShopmallDataRspOuterClass.GetShopmallDataRsp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PacketGetShopmallDataRsp extends BasePacket {
|
||||
|
||||
public PacketGetShopmallDataRsp() {
|
||||
super(PacketOpcodes.GetShopmallDataRsp);
|
||||
|
||||
GetShopmallDataRsp proto = GetShopmallDataRsp.newBuilder().build();
|
||||
|
||||
List<Integer> shop_malls = List.of(900, 1052, 902, 1001, 903);
|
||||
|
||||
GetShopmallDataRsp proto = GetShopmallDataRsp.newBuilder()
|
||||
.addAllShopTypeList(shop_malls)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,13 @@ import emu.grasscutter.net.proto.McoinExchangeHcoinRspOuterClass;
|
||||
|
||||
public class PacketMcoinExchangeHcoinRsp extends BasePacket {
|
||||
|
||||
public PacketMcoinExchangeHcoinRsp(int mcoin, int hcoin) {
|
||||
public PacketMcoinExchangeHcoinRsp(int retCode) {
|
||||
super(PacketOpcodes.McoinExchangeHcoinRsp);
|
||||
|
||||
McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp mcoinExchangeHcoinRsp = McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp.newBuilder()
|
||||
.setMCoinNum(mcoin)
|
||||
.setHCoinNum(hcoin).build();
|
||||
McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp proto = McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp.newBuilder()
|
||||
.setRetcode(retCode)
|
||||
.build();
|
||||
|
||||
this.setData(mcoinExchangeHcoinRsp);
|
||||
this.setData(proto);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user