Compare commits

...

15 Commits

Author SHA1 Message Date
Kurtivan2223
992742d746 Proxy Update
Now you can Finally open sites, use apps that requires internet access which typically mitmproxy intercepts
2022-06-08 05:44:01 -07:00
ImmuState
6149e326ba Make UI for blueprint unlocking behave. 2022-06-08 05:43:38 -07:00
ImmuState
51637ab01e Make calculation for partial claims better. 2022-06-08 05:43:38 -07:00
ImmuState
840bea2b0f Make the UI behave. 2022-06-08 05:43:38 -07:00
ImmuState
e30516d698 Send periodic notifications to the client for ongoing forges. 2022-06-08 05:43:38 -07:00
ImmuState
36e7e028f7 Add ForgeQueueDataNotify, unwhack UI. 2022-06-08 05:43:38 -07:00
ImmuState
e193781f0c Cancel forging and refund items. 2022-06-08 05:43:38 -07:00
ImmuState
5a58a0b765 Obtain forging results (UI is broken though). 2022-06-08 05:43:38 -07:00
ImmuState
8a3c25ff8e Consume material. 2022-06-08 05:43:38 -07:00
ImmuState
8840f22bba Check materials and mora 2022-06-08 05:43:38 -07:00
ImmuState
aae799f9a7 Start a new forge (no checking for now). 2022-06-08 05:43:38 -07:00
ImmuState
4ffda39b12 Add handling for ForgeExcelConfigData. 2022-06-08 05:43:38 -07:00
ImmuState
d7a4209138 Change active forges in player to list. 2022-06-08 05:43:38 -07:00
ImmuState
47bcfe96f6 Add missing forging protos, add data for ongoing forges to player, handler for ForgeStartReq. 2022-06-08 05:43:38 -07:00
ImmuState
0c0719bd2a Move forging stuff to ForgingManager, make client respond to ForgeGetQueueDataReq and actually display forging queues. 2022-06-08 05:43:38 -07:00
22 changed files with 880 additions and 28 deletions

View File

@ -0,0 +1,14 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ForgeQueueData.proto";
// CmdId: 628
// EnetChannelId: 0
// EnetIsReliable: true
message ForgeGetQueueDataRsp {
int32 retcode = 15;
uint32 max_queue_num = 13;
map<uint32, ForgeQueueData> forge_queue_map = 11;
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ForgeQueueData.proto";
// CmdId: 633
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message ForgeQueueDataNotify {
map<uint32, ForgeQueueData> forge_queue_map = 14;
repeated uint32 removed_forge_queue_list = 8;
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ForgeQueueManipulateType.proto";
// CmdId: 659
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message ForgeQueueManipulateReq {
uint32 forge_queue_id = 11;
ForgeQueueManipulateType manipulate_type = 7;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ForgeQueueManipulateType.proto";
import "ItemParam.proto";
// CmdId: 684
// EnetChannelId: 0
// EnetIsReliable: true
message ForgeQueueManipulateRsp {
int32 retcode = 13;
ForgeQueueManipulateType manipulate_type = 8;
repeated ItemParam output_item_list = 6;
repeated ItemParam return_item_list = 10;
repeated ItemParam extra_output_item_list = 3;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
enum ForgeQueueManipulateType {
FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT = 0;
FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE = 1;
}

13
proto/ForgeStartReq.proto Normal file
View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 676
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message ForgeStartReq {
uint32 forge_id = 9;
uint32 forge_count = 11;
uint32 avatar_id = 13;
}

10
proto/ForgeStartRsp.proto Normal file
View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 672
// EnetChannelId: 0
// EnetIsReliable: true
message ForgeStartRsp {
int32 retcode = 11;
}

View File

@ -20,7 +20,12 @@
#
##
from mitmproxy import http
import collections
import random
from mitmproxy import http, connection, ctx, tls
from abc import ABC, abstractmethod
from enum import Enum
from mitmproxy.utils import human
from proxy_config import USE_SSL
from proxy_config import REMOTE_HOST
from proxy_config import REMOTE_PORT
@ -71,6 +76,80 @@ class MlgmXyysd_Genshin_Impact_Proxy:
flow.request.host = REMOTE_HOST
flow.request.port = REMOTE_PORT
class InterceptionResult(Enum):
SUCCESS = 1
FAILURE = 2
SKIPPED = 3
class TlsStrategy(ABC):
def __init__(self):
self.history = collections.defaultdict(lambda: collections.deque(maxlen=200))
@abstractmethod
def should_intercept(self, server_address: connection.Address) -> bool:
raise NotImplementedError()
def record_success(self, server_address):
self.history[server_address].append(InterceptionResult.SUCCESS)
def record_failure(self, server_address):
self.history[server_address].append(InterceptionResult.FAILURE)
def record_skipped(self, server_address):
self.history[server_address].append(InterceptionResult.SKIPPED)
class ConservativeStrategy(TlsStrategy):
def should_intercept(self, server_address: connection.Address) -> bool:
return InterceptionResult.FAILURE not in self.history[server_address]
class ProbabilisticStrategy(TlsStrategy):
def __init__(self, p: float):
self.p = p
super().__init__()
def should_intercept(self, server_address: connection.Address) -> bool:
return random.uniform(0, 1) < self.p
class MaybeTls:
strategy: TlsStrategy
def load(self, l):
l.add_option(
"tls_strategy", int, 0,
"TLS passthrough strategy. If set to 0, connections will be passed through after the first unsuccessful "
"handshake. If set to 0 < p <= 100, connections with be passed through with probability p.",
)
def configure(self, updated):
if "tls_strategy" not in updated:
return
if ctx.options.tls_strategy > 0:
self.strategy = ProbabilisticStrategy(ctx.options.tls_strategy / 100)
else:
self.strategy = ConservativeStrategy()
def tls_clienthello(self, data: tls.ClientHelloData):
server_address = data.context.server.peername
if not self.strategy.should_intercept(server_address):
ctx.log(f"TLS passthrough: {human.format_address(server_address)}.")
data.ignore_connection = True
self.strategy.record_skipped(server_address)
def tls_established_client(self, data: tls.TlsData):
server_address = data.context.server.peername
ctx.log(f"TLS handshake successful: {human.format_address(server_address)}")
self.strategy.record_success(server_address)
def tls_failed_client(self, data: tls.TlsData):
server_address = data.context.server.peername
ctx.log(f"TLS handshake failed: {human.format_address(server_address)}")
self.strategy.record_failure(server_address)
addons = [
MlgmXyysd_Genshin_Impact_Proxy()
MlgmXyysd_Genshin_Impact_Proxy(),
MaybeTls()
]

View File

@ -87,6 +87,7 @@ public class GameData {
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<ForgeData> forgeDataMap = new Int2ObjectOpenHashMap<>();
// Cache
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
@ -367,4 +368,8 @@ public class GameData {
public static Int2ObjectMap<QuestData> getQuestDataMap() {
return questDataMap;
}
public static Int2ObjectMap<ForgeData> getForgeDataMap() {
return forgeDataMap;
}
}

View File

@ -0,0 +1,68 @@
package emu.grasscutter.data.excels;
import java.util.List;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.common.OpenCondData;
@ResourceType(name = {"ForgeExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST)
public class ForgeData extends GameResource {
private int id;
private int playerLevel;
private int forgeType;
private int resultItemId;
private int resultItemCount;
private int forgeTime;
private int queueNum;
private int scoinCost;
private int priority;
private List<ItemParamData> materialItems;
@Override
public int getId() {
return this.id;
}
public int getPlayerLevel() {
return playerLevel;
}
public int getForgeType() {
return forgeType;
}
public int getResultItemId() {
return resultItemId;
}
public int getResultItemCount() {
return resultItemCount;
}
public int getForgeTime() {
return forgeTime;
}
public int getQueueNum() {
return queueNum;
}
public int getScoinCost() {
return scoinCost;
}
public int getPriority() {
return priority;
}
public List<ItemParamData> getMaterialItems() {
return materialItems;
}
@Override
public void onLoad() {
}
}

View File

@ -0,0 +1,92 @@
package emu.grasscutter.game.managers.ForgingManager;
import dev.morphia.annotations.Entity;
import emu.grasscutter.utils.Utils;
@Entity
public class ActiveForgeData {
private int forgeId;
private int avatarId;
private int count;
private int startTime;
private int forgeTime;
private int lastUnfinishedCount;
private boolean changed;
public int getFinishedCount(int currentTime) {
int timeDelta = currentTime - this.startTime;
int finishedCount = (int)Math.floor(timeDelta / this.forgeTime);
return Math.min(finishedCount, this.count);
}
public int getUnfinishedCount(int currentTime) {
return this.count - this.getFinishedCount(currentTime);
}
public int getNextFinishTimestamp(int currentTime) {
return
(currentTime >= this.getTotalFinishTimestamp())
? this.getTotalFinishTimestamp()
: (this.getFinishedCount(currentTime) * this.forgeTime + this.forgeTime + this.startTime);
}
public int getTotalFinishTimestamp() {
return this.startTime + this.forgeTime * this.count;
}
public int getForgeId() {
return this.forgeId;
}
public void setForgeId(int value) {
this.forgeId = value;
}
public int getAvatarId() {
return this.avatarId;
}
public void setAvatarId(int value) {
this.avatarId = value;
}
public int getCount() {
return count;
}
public void setCount(int value) {
this.count = value;
}
public int getStartTime() {
return this.startTime;
}
public void setStartTime(int value) {
this.startTime = value;
}
public int getForgeTime() {
return this.forgeTime;
}
public void setForgeTime(int value) {
this.forgeTime = value;
}
public boolean isChanged() {
return this.changed;
}
public void setChanged(boolean value) {
this.changed = value;
}
public boolean updateChanged(int currentTime) {
int currentUnfinished = this.getUnfinishedCount(currentTime);
if (currentUnfinished != this.lastUnfinishedCount) {
this.changed = true;
this.lastUnfinishedCount = currentUnfinished;
}
return this.changed;
}
}

View File

@ -0,0 +1,331 @@
package emu.grasscutter.game.managers.ForgingManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.mongodb.QueryBuilder;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.ForgeData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.ForgeStartReqOuterClass;
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
import emu.grasscutter.net.proto.ForgeQueueManipulateTypeOuterClass.ForgeQueueManipulateType;
import emu.grasscutter.net.proto.ForgeStartReqOuterClass.ForgeStartReq;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.server.packet.send.PacketForgeDataNotify;
import emu.grasscutter.server.packet.send.PacketForgeFormulaDataNotify;
import emu.grasscutter.server.packet.send.PacketForgeGetQueueDataRsp;
import emu.grasscutter.server.packet.send.PacketForgeQueueDataNotify;
import emu.grasscutter.server.packet.send.PacketForgeQueueManipulateRsp;
import emu.grasscutter.server.packet.send.PacketForgeStartRsp;
import emu.grasscutter.utils.Utils;
public class ForgingManager {
private final Player player;
public ForgingManager(Player player) {
this.player = player;
}
/**********
Blueprint unlocking.
**********/
public synchronized boolean unlockForgingBlueprint(GameItem blueprintItem) {
// Make sure this is actually a forging blueprint.
if (!blueprintItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) {
return false;
}
// Determine the forging item we should unlock.
int forgeId = Integer.parseInt(blueprintItem.getItemData().getItemUse().get(0).getUseParam().get(0));
// Remove the blueprint from the player's inventory.
// We need to do this here, before sending ForgeFormulaDataNotify, or the the forging UI won't correctly
// update when unlocking the blueprint.
player.getInventory().removeItem(blueprintItem, 1);
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
this.player.getUnlockedForgingBlueprints().add(forgeId);
this.player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
return true;
}
/**********
Communicate forging information to the client.
**********/
private synchronized int determineNumberOfQueues() {
int adventureRank = player.getLevel();
return
(adventureRank >= 15) ? 4
: (adventureRank >= 10) ? 3
: (adventureRank >= 5) ? 2
: 1;
}
private synchronized Map<Integer, ForgeQueueData> determineCurrentForgeQueueData() {
Map<Integer, ForgeQueueData> res = new HashMap<>();
int currentTime = Utils.getCurrentSeconds();
// Create queue information for all active forges.
for (int i = 0; i < this.player.getActiveForges().size(); i++) {
ActiveForgeData activeForge = this.player.getActiveForges().get(i);
ForgeQueueData data = ForgeQueueData.newBuilder()
.setQueueId(i + 1)
.setForgeId(activeForge.getForgeId())
.setFinishCount(activeForge.getFinishedCount(currentTime))
.setUnfinishCount(activeForge.getUnfinishedCount(currentTime))
.setTotalFinishTimestamp(activeForge.getTotalFinishTimestamp())
.setNextFinishTimestamp(activeForge.getNextFinishTimestamp(currentTime))
.setAvatarId(activeForge.getAvatarId())
.build();
res.put(i + 1, data);
}
return res;
}
public synchronized void sendForgeDataNotify() {
// Determine the number of queues and unlocked items.
int numQueues = this.determineNumberOfQueues();
var unlockedItems = this.player.getUnlockedForgingBlueprints();
var queueData = this.determineCurrentForgeQueueData();
// Send notification.
this.player.sendPacket(new PacketForgeDataNotify(unlockedItems, numQueues, queueData));
}
public synchronized void handleForgeGetQueueDataReq() {
// Determine the number of queues.
int numQueues = this.determineNumberOfQueues();
var queueData = this.determineCurrentForgeQueueData();
// Reply.
this.player.sendPacket(new PacketForgeGetQueueDataRsp(Retcode.RET_SUCC, numQueues, queueData));
}
/**********
Initiate forging process.
**********/
private synchronized void sendForgeQueueDataNotify() {
var queueData = this.determineCurrentForgeQueueData();
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
private synchronized void sendForgeQueueDataNotify(boolean hasRemoved) {
var queueData = this.determineCurrentForgeQueueData();
if (hasRemoved) {
this.player.sendPacket(new PacketForgeQueueDataNotify(Map.of(), List.of(1, 2, 3, 4)));
}
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
public synchronized void handleForgeStartReq(ForgeStartReq req) {
// Refuse if all queues are already full.
if (this.player.getActiveForges().size() >= this.determineNumberOfQueues()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_QUEUE_FULL));
return;
}
// Get the required forging information for the target item.
if (!GameData.getForgeDataMap().containsKey(req.getForgeId())) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FAIL)); //ToDo: Probably the wrong return code.
return;
}
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
// Check if we have enough of each material.
for (var material : forgeData.getMaterialItems()) {
if (material.getItemId() == 0) {
continue;
}
int currentCount = this.player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(material.getItemId()).getCount();
if (currentCount < material.getCount() * req.getForgeCount()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
return;
}
}
// Check if we have enough Mora.
if (this.player.getMora() < forgeData.getScoinCost() * req.getForgeCount()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
return;
}
// Consume material and Mora.
for (var material : forgeData.getMaterialItems()) {
if (material.getItemId() == 0) {
continue;
}
GameItem item = this.player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(material.getItemId());
this.player.getInventory().removeItem(item, material.getCount() * req.getForgeCount());
}
this.player.setMora(this.player.getMora() - forgeData.getScoinCost() * req.getForgeCount());
// Create and add active forge.
ActiveForgeData activeForge = new ActiveForgeData();
activeForge.setForgeId(req.getForgeId());
activeForge.setAvatarId(req.getAvatarId());
activeForge.setCount(req.getForgeCount());
activeForge.setStartTime(Utils.getCurrentSeconds());
activeForge.setForgeTime(forgeData.getForgeTime());
this.player.getActiveForges().add(activeForge);
// Done.
this.sendForgeQueueDataNotify();
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_SUCC));
}
/**********
Forge queue manipulation (obtaining results and cancelling forges).
**********/
private synchronized void obtainItems(int queueId) {
// Determin how many items are finished.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
int finished = forge.getFinishedCount(currentTime);
int unfinished = forge.getUnfinishedCount(currentTime);
// Sanity check: Are any items finished?
if (finished <= 0) {
return;
}
// Give finished items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
ItemData resultItemData = GameData.getItemDataMap().get(data.getResultItemId());
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
this.player.getInventory().addItem(addItem);
// Replace active forge with a new one for the unfinished items, if there are any.
if (unfinished > 0) {
ActiveForgeData remainingForge = new ActiveForgeData();
remainingForge.setForgeId(forge.getForgeId());
remainingForge.setAvatarId(forge.getAvatarId());
remainingForge.setCount(unfinished);
remainingForge.setForgeTime(forge.getForgeTime());
remainingForge.setStartTime(forge.getStartTime() + finished * forge.getForgeTime());
this.player.getActiveForges().set(queueId - 1, remainingForge);
this.sendForgeQueueDataNotify();
}
// Otherwise, completely remove it.
else {
this.player.getActiveForges().remove(queueId - 1);
// this.sendForgeQueueDataNotify(queueId);
this.sendForgeQueueDataNotify(true);
}
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT, List.of(addItem), List.of(), List.of()));
}
private synchronized void cancelForge(int queueId) {
// Make sure there are no unfinished items.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
if (forge.getFinishedCount(currentTime) > 0) {
return;
}
// Return material items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
var returnItems = new ArrayList<GameItem>();
for (var material : data.getMaterialItems()) {
if (material.getItemId() == 0) {
continue;
}
ItemData resultItemData = GameData.getItemDataMap().get(material.getItemId());
GameItem returnItem = new GameItem(resultItemData, material.getItemCount() * forge.getCount());
this.player.getInventory().addItem(returnItem);
returnItems.add(returnItem);
}
// Return Mora to the player.
this.player.setMora(this.player.getMora() + data.getScoinCost() * forge.getCount());
ItemData moraItem = GameData.getItemDataMap().get(202);
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
returnItems.add(returnMora);
// Remove the forge queue.
this.player.getActiveForges().remove(queueId - 1);
this.sendForgeQueueDataNotify(true);
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE, List.of(), returnItems, List.of()));
}
public synchronized void handleForgeQueueManipulateReq(ForgeQueueManipulateReq req) {
// Get info from the request.
int queueId = req.getForgeQueueId();
var manipulateType = req.getManipulateType();
// Handle according to the manipulation type.
switch (manipulateType) {
case FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT:
this.obtainItems(queueId);
break;
case FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE:
this.cancelForge(queueId);
break;
default:
break; //Should never happen.
}
}
/**********
Periodic forging updates.
**********/
public synchronized void sendPlayerForgingUpdate() {
int currentTime = Utils.getCurrentSeconds();
// Determine if sending an update is necessary.
// We only send an update if there are forges in the forge queue
// that have changed since the last notification.
if (this.player.getActiveForges().size() <= 0) {
return;
}
boolean hasChanges = this.player.getActiveForges().stream()
.filter(forge -> forge.updateChanged(currentTime))
.findAny()
.isPresent();
if (!hasChanges) {
return;
}
// Send notification.
this.sendForgeQueueDataNotify();
// Reset changed flags.
this.player.getActiveForges().stream()
.forEach(forge -> forge.setChanged(false));
}
}

View File

@ -822,6 +822,7 @@ public class InventoryManager {
}
int used = 0;
boolean useSuccess = false;
// Use
switch (useItem.getItemData().getMaterialType()) {
@ -852,15 +853,8 @@ public class InventoryManager {
// Handle forging blueprints.
if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) {
// Determine the forging item we should unlock.
int forgeId = Integer.parseInt(useItem.getItemData().getItemUse().get(0).getUseParam().get(0));
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
player.getUnlockedForgingBlueprints().add(forgeId);
// Use up the blueprint item.
used = 1;
// Unlock.
useSuccess = player.getForgingManager().unlockForgingBlueprint(useItem);
}
break;
case MATERIAL_CHEST:
@ -927,10 +921,15 @@ public class InventoryManager {
used = 1;
}
// If we used at least one item, or one of the methods called here reports using the item successfully,
// we return the item to make UseItemRsp a success.
if (used > 0) {
player.getInventory().removeItem(useItem, used);
return useItem;
}
if (useSuccess) {
return useItem;
}
return null;
}

View File

@ -30,6 +30,8 @@ import emu.grasscutter.game.managers.InsectCaptureManager;
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
import emu.grasscutter.game.managers.SotSManager;
import emu.grasscutter.game.managers.EnergyManager.EnergyManager;
import emu.grasscutter.game.managers.ForgingManager.ActiveForgeData;
import emu.grasscutter.game.managers.ForgingManager.ForgingManager;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.PlayerProperty;
@ -91,6 +93,7 @@ public class Player {
private Set<Integer> flyCloakList;
private Set<Integer> costumeList;
private Set<Integer> unlockedForgingBlueprints;
private List<ActiveForgeData> activeForges;
private Integer widgetId;
@ -153,6 +156,7 @@ public class Player {
@Transient private MapMarksManager mapMarksManager;
@Transient private StaminaManager staminaManager;
@Transient private EnergyManager energyManager;
@Transient private ForgingManager forgingManager;
@Transient private DeforestationManager deforestationManager;
private long springLastUsed;
@ -186,6 +190,7 @@ public class Player {
this.flyCloakList = new HashSet<>();
this.costumeList = new HashSet<>();
this.unlockedForgingBlueprints = new HashSet<>();
this.activeForges = new ArrayList<>();
this.setSceneId(3);
this.setRegionId(1);
@ -209,6 +214,7 @@ public class Player {
this.staminaManager = new StaminaManager(this);
this.sotsManager = new SotSManager(this);
this.energyManager = new EnergyManager(this);
this.forgingManager = new ForgingManager(this);
}
// On player creation
@ -240,6 +246,7 @@ public class Player {
this.sotsManager = new SotSManager(this);
this.energyManager = new EnergyManager(this);
this.deforestationManager = new DeforestationManager(this);
this.forgingManager = new ForgingManager(this);
}
public int getUid() {
@ -519,7 +526,11 @@ public class Player {
}
public Set<Integer> getUnlockedForgingBlueprints() {
return unlockedForgingBlueprints;
return this.unlockedForgingBlueprints;
}
public List<ActiveForgeData> getActiveForges() {
return this.activeForges;
}
public MpSettingType getMpSetting() {
@ -1120,6 +1131,10 @@ public class Player {
return this.energyManager;
}
public ForgingManager getForgingManager() {
return this.forgingManager;
}
public AbilityManager getAbilityManager() {
return abilityManager;
}
@ -1179,6 +1194,9 @@ public class Player {
this.save();
this.sendPacket(new PacketAvatarExpeditionDataNotify(this));
}
// Send updated forge queue data, if necessary.
this.getForgingManager().sendPlayerForgingUpdate();
}
@ -1265,7 +1283,7 @@ public class Player {
session.send(new PacketWidgetGadgetAllDataNotify());
session.send(new PacketPlayerHomeCompInfoNotify(this));
session.send(new PacketHomeComfortInfoNotify(this));
session.send(new PacketForgeDataNotify(this));
this.forgingManager.sendForgeDataNotify();
getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.

View File

@ -0,0 +1,14 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.ForgeGetQueueDataReq)
public class HandlerForgeGetQueueDataReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.getPlayer().getForgingManager().handleForgeGetQueueDataReq();
}
}

View File

@ -0,0 +1,16 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.ForgeQueueManipulateReq)
public class HandlerForgeQueueManipulateReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ForgeQueueManipulateReq req = ForgeQueueManipulateReq.parseFrom(payload);
session.getPlayer().getForgingManager().handleForgeQueueManipulateReq(req);
}
}

View File

@ -0,0 +1,23 @@
package emu.grasscutter.server.packet.recv;
import java.lang.invoke.StringConcatFactory;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ForgeStartReqOuterClass;
import emu.grasscutter.net.proto.DeleteFriendReqOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketDelMailRsp;
@Opcodes(PacketOpcodes.ForgeStartReq)
public class HandlerForgeStartReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ForgeStartReqOuterClass.ForgeStartReq req = ForgeStartReqOuterClass.ForgeStartReq.parseFrom(payload);
session.getPlayer().getForgingManager().handleForgeStartReq(req);
}
}

View File

@ -1,30 +1,27 @@
package emu.grasscutter.server.packet.send;
import java.util.Map;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ForgeDataNotifyOuterClass.ForgeDataNotify;
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
public class PacketForgeDataNotify extends BasePacket {
public PacketForgeDataNotify(Player player) {
public PacketForgeDataNotify(Iterable<Integer> unlockedItem, int numQueues, Map<Integer, ForgeQueueData> queueData) {
super(PacketOpcodes.ForgeDataNotify);
int adventureRank = player.getLevel();
int numQueues =
(adventureRank >= 15) ? 4
: (adventureRank >= 10) ? 3
: (adventureRank >= 5) ? 2
: 1;
ForgeDataNotify.Builder builder = ForgeDataNotify.newBuilder()
.addAllForgeIdList(unlockedItem)
.setMaxQueueNum(numQueues);
ForgeDataNotify proto = ForgeDataNotify.newBuilder()
.addAllForgeIdList(player.getUnlockedForgingBlueprints())
.setMaxQueueNum(numQueues)
.build();
for (int queueId : queueData.keySet()) {
var data = queueData.get(queueId);
builder.putForgeQueueMap(queueId, data);
}
// ToDo: Add the information for the actual forging queues
// and ongoing forges.
this.setData(proto);
this.setData(builder.build());
}
}

View File

@ -0,0 +1,27 @@
package emu.grasscutter.server.packet.send;
import java.util.Map;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ForgeGetQueueDataRspOuterClass.ForgeGetQueueDataRsp;
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
public class PacketForgeGetQueueDataRsp extends BasePacket {
public PacketForgeGetQueueDataRsp(Retcode retcode, int numQueues, Map<Integer, ForgeQueueData> queueData) {
super(PacketOpcodes.ForgeGetQueueDataRsp);
ForgeGetQueueDataRsp.Builder builder = ForgeGetQueueDataRsp.newBuilder()
.setRetcode(retcode.getNumber())
.setMaxQueueNum(numQueues);
for (int queueId : queueData.keySet()) {
var data = queueData.get(queueId);
builder.putForgeQueueMap(queueId, data);
}
this.setData(builder.build());
}
}

View File

@ -0,0 +1,26 @@
package emu.grasscutter.server.packet.send;
import java.util.List;
import java.util.Map;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ForgeQueueDataNotifyOuterClass.ForgeQueueDataNotify;
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
public class PacketForgeQueueDataNotify extends BasePacket {
public PacketForgeQueueDataNotify(Map<Integer, ForgeQueueData> queueData, List<Integer> removedQueues) {
super(PacketOpcodes.ForgeQueueDataNotify);
ForgeQueueDataNotify.Builder builder = ForgeQueueDataNotify.newBuilder()
.addAllRemovedForgeQueueList(removedQueues);
for (int queueId : queueData.keySet()) {
var data = queueData.get(queueId);
builder.putForgeQueueMap(queueId, data);
}
this.setData(builder.build());
}
}

View File

@ -0,0 +1,44 @@
package emu.grasscutter.server.packet.send;
import java.util.List;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ForgeQueueManipulateRspOuterClass.ForgeQueueManipulateRsp;
import emu.grasscutter.net.proto.ForgeQueueManipulateTypeOuterClass.ForgeQueueManipulateType;
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
public class PacketForgeQueueManipulateRsp extends BasePacket {
public PacketForgeQueueManipulateRsp(Retcode retcode, ForgeQueueManipulateType type, List<GameItem> output, List<GameItem> refund, List<GameItem> extra) {
super(PacketOpcodes.ForgeQueueManipulateRsp);
ForgeQueueManipulateRsp.Builder builder = ForgeQueueManipulateRsp.newBuilder()
.setRetcode(retcode.getNumber())
.setManipulateType(type);
for (GameItem item : output) {
ItemParam toAdd = ItemParam.newBuilder()
.setItemId(item.getItemId())
.setCount(item.getCount())
.build();
builder.addOutputItemList(toAdd);
}
for (GameItem item : refund) {
ItemParam toAdd = ItemParam.newBuilder()
.setItemId(item.getItemId())
.setCount(item.getCount())
.build();
builder.addReturnItemList(toAdd);
}
// ToDo: Add extra items when once we have handling for it.
this.setData(builder.build());
}
}

View File

@ -0,0 +1,23 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ForgeStartRspOuterClass.ForgeStartRsp;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import java.util.ArrayList;
import java.util.List;
public class PacketForgeStartRsp extends BasePacket {
public PacketForgeStartRsp(Retcode retcode) {
super(PacketOpcodes.ForgeStartRsp);
ForgeStartRsp proto = ForgeStartRsp.newBuilder()
.setRetcode(retcode.getNumber())
.build();
this.setData(proto);
}
}