mirror of
https://github.com/Melledy/Nebula.git
synced 2026-09-19 09:09:54 +02:00
Fix cant claim beginner banner results and simplify newbie gacha flow
This commit is contained in:
@@ -45,20 +45,20 @@ public class GachaManager extends PlayerManager implements GameDatabaseObject {
|
|||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized NewbieObtainLock lockNewbieObtain(int newbieId) {
|
public synchronized boolean tryLockNewbieObtain(int newbieId) {
|
||||||
if (this.lockedNewbieObtainIds.contains(newbieId)) {
|
if (this.lockedNewbieObtainIds.contains(newbieId)) {
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lockedNewbieObtainIds.add(newbieId);
|
this.lockedNewbieObtainIds.add(newbieId);
|
||||||
return new NewbieObtainLockHandle(newbieId);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean isNewbieObtainLocked(int newbieId) {
|
public synchronized boolean isNewbieObtainLocked(int newbieId) {
|
||||||
return this.lockedNewbieObtainIds.contains(newbieId);
|
return this.lockedNewbieObtainIds.contains(newbieId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void unlockNewbieObtainInternal(int newbieId) {
|
public synchronized void unlockNewbieObtain(int newbieId) {
|
||||||
this.lockedNewbieObtainIds.remove(newbieId);
|
this.lockedNewbieObtainIds.remove(newbieId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,30 +145,4 @@ public class GachaManager extends PlayerManager implements GameDatabaseObject {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class NewbieObtainLockHandle implements NewbieObtainLock {
|
|
||||||
private final int newbieId;
|
|
||||||
private boolean closed;
|
|
||||||
|
|
||||||
private NewbieObtainLockHandle(int newbieId) {
|
|
||||||
this.newbieId = newbieId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
synchronized (GachaManager.this) {
|
|
||||||
if (this.closed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.closed = true;
|
|
||||||
unlockNewbieObtainInternal(this.newbieId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface NewbieObtainLock extends AutoCloseable {
|
|
||||||
@Override
|
|
||||||
void close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class GachaModule extends GameContextModule {
|
public class GachaModule extends GameContextModule {
|
||||||
private final NewbieGachaModule newbieGachaModule = new NewbieGachaModule();
|
|
||||||
|
private final NewbieGachaModule newbieGachaModule;
|
||||||
|
|
||||||
public GachaModule(GameContext context) {
|
public GachaModule(GameContext context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
this.newbieGachaModule = new NewbieGachaModule();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GachaResult spin(Player player, int bannerId, int amount) {
|
public GachaResult spin(Player player, int bannerId, int amount) {
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ import java.util.List;
|
|||||||
|
|
||||||
public final class NewbieGachaModule {
|
public final class NewbieGachaModule {
|
||||||
|
|
||||||
private record NewbieRequest(GachaManager manager, GachaNewbieDef newbieDef) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<GachaNewbieInfo> listInfos(Player player) {
|
public List<GachaNewbieInfo> listInfos(Player player) {
|
||||||
var newbieDefs = GameData.getGachaNewbieDataTable().values();
|
var newbieDefs = GameData.getGachaNewbieDataTable().values();
|
||||||
var infos = new ArrayList<GachaNewbieInfo>(newbieDefs.size());
|
var infos = new ArrayList<GachaNewbieInfo>(newbieDefs.size());
|
||||||
@@ -54,21 +51,20 @@ public final class NewbieGachaModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int[] spin(Player player, int newbieId) {
|
public int[] spin(Player player, int newbieId) {
|
||||||
var request = resolveRequest(player, newbieId);
|
GachaNewbieDef newbieDef = GameData.getGachaNewbieDataTable().get(newbieId);
|
||||||
if (request == null) {
|
if (newbieDef == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int newbieStateId = request.newbieDef().getId();
|
int newbieStateId = newbieDef.getId();
|
||||||
var bannerDef = GameData.getGachaDataTable().get(newbieStateId);
|
var bannerDef = GameData.getGachaDataTable().get(newbieStateId);
|
||||||
if (bannerDef == null) {
|
if (bannerDef == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var manager = request.manager();
|
synchronized (player.getGachaManager()) {
|
||||||
synchronized (manager) {
|
NewbieGachaState state = player.getGachaManager().getOrCreateNewbieState(newbieDef);
|
||||||
var state = loadStateForSpin(manager, request.newbieDef());
|
if (player.getGachaManager().isNewbieObtainLocked(newbieStateId) || !state.canSpin()) {
|
||||||
if (state == null) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,56 +77,52 @@ public final class NewbieGachaModule {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.saveNewbieState(state);
|
player.getGachaManager().saveNewbieState(state);
|
||||||
return cards;
|
return cards;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean save(Player player, int newbieId, Integer index) {
|
public boolean save(Player player, int newbieId, Integer index) {
|
||||||
int resolvedIndex = index == null ? -1 : index;
|
var newbieDef = GameData.getGachaNewbieDataTable().get(newbieId);
|
||||||
var request = resolveRequest(player, newbieId);
|
if (newbieDef == null) {
|
||||||
if (request == null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var manager = request.manager();
|
synchronized (player.getGachaManager()) {
|
||||||
synchronized (manager) {
|
if (player.getGachaManager().isNewbieObtainLocked(newbieId)) {
|
||||||
var state = loadStateForSave(manager, request.newbieDef());
|
return false;
|
||||||
if (state == null) {
|
}
|
||||||
|
var state = player.getGachaManager().getOrCreateNewbieState(newbieDef);
|
||||||
|
if (state == null || !state.canSavePendingResult()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.savePendingResult(resolvedIndex)) {
|
if (!state.savePendingResult(index)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.saveNewbieState(state);
|
player.getGachaManager().saveNewbieState(state);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlayerChangeInfo obtain(Player player, int newbieId, int index) {
|
public PlayerChangeInfo obtain(Player player, int newbieId, int index) {
|
||||||
if (index < 0) {
|
GachaNewbieDef gachaNewbieDef = GameData.getGachaNewbieDataTable().get(newbieId);
|
||||||
|
if (gachaNewbieDef == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = resolveRequest(player, newbieId);
|
int newbieStateId = gachaNewbieDef.getId();
|
||||||
if (request == null) {
|
if (!player.getGachaManager().tryLockNewbieObtain(newbieStateId)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int newbieStateId = request.newbieDef().getId();
|
try {
|
||||||
var obtainLock = request.manager().lockNewbieObtain(newbieStateId);
|
|
||||||
if (obtainLock == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try (var ignored = obtainLock) {
|
|
||||||
NewbieGachaState state;
|
NewbieGachaState state;
|
||||||
int[] cards;
|
int[] cards;
|
||||||
synchronized (request.manager()) {
|
synchronized (player.getGachaManager()) {
|
||||||
state = loadStateForObtain(request.manager(), request.newbieDef(), index);
|
state = player.getGachaManager().findNewbieState(newbieStateId);
|
||||||
if (state == null) {
|
if (state == null || !state.canObtain(index)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,66 +139,19 @@ public final class NewbieGachaModule {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (request.manager()) {
|
synchronized (player.getGachaManager()) {
|
||||||
if (!state.markReceived(index)) {
|
if (!state.markReceived(index)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
request.manager().saveNewbieState(state);
|
player.getGachaManager().saveNewbieState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
GachaRewardResolver.apply(player, rewardPlan, change);
|
GachaRewardResolver.apply(player, rewardPlan, change);
|
||||||
return change;
|
return change;
|
||||||
|
} finally {
|
||||||
|
player.getGachaManager().unlockNewbieObtain(newbieStateId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private NewbieRequest resolveRequest(Player player, int newbieId) {
|
|
||||||
var newbieDef = GameData.getGachaNewbieDataTable().get(newbieId);
|
|
||||||
if (newbieDef == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new NewbieRequest(player.getGachaManager(), newbieDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
private NewbieGachaState loadStateForSpin(GachaManager manager, GachaNewbieDef newbieDef) {
|
|
||||||
int newbieStateId = newbieDef.getId();
|
|
||||||
if (manager.isNewbieObtainLocked(newbieStateId)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var state = manager.getOrCreateNewbieState(newbieDef);
|
|
||||||
if (!state.canSpin(false)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NewbieGachaState loadStateForSave(GachaManager manager,
|
|
||||||
GachaNewbieDef newbieDef) {
|
|
||||||
int newbieStateId = newbieDef.getId();
|
|
||||||
if (manager.isNewbieObtainLocked(newbieStateId)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var state = manager.getOrCreateNewbieState(newbieDef);
|
|
||||||
if (!state.canSavePendingResult(false)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NewbieGachaState loadStateForObtain(GachaManager manager, GachaNewbieDef newbieDef, int index) {
|
|
||||||
int newbieStateId = newbieDef.getId();
|
|
||||||
var state = manager.findNewbieState(newbieStateId);
|
|
||||||
if (state == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!state.canObtain(index)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,25 +30,23 @@ public class NewbieGachaState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the player can perform a spin.
|
// Checks if the player can perform a spin.
|
||||||
public boolean canSpin(boolean obtainLocked) {
|
public boolean canSpin() {
|
||||||
return !received && !obtainLocked && remainingSpinCount > 0;
|
return !received && remainingSpinCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the current pending result can be moved to saved results
|
// Checks if the current pending result can be moved to saved results
|
||||||
public boolean canSavePendingResult(boolean obtainLocked) {
|
public boolean canSavePendingResult() {
|
||||||
return !received && !obtainLocked && hasPendingResult();
|
return !received && hasPendingResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates the maximum allowed saved results.
|
// Updates the maximum allowed saved results.
|
||||||
public boolean applyConfig(int saveCount) {
|
public void applyConfig(int saveCount) {
|
||||||
int oldSaveCount = this.saveCount;
|
|
||||||
this.saveCount = Math.max(1, saveCount);
|
this.saveCount = Math.max(1, saveCount);
|
||||||
return this.saveCount != oldSaveCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Applies a new spin result to the pending slot and consumes a spin attempt
|
// Applies a new spin result to the pending slot and consumes a spin attempt
|
||||||
public boolean applySpinResult(int[] cards) {
|
public boolean applySpinResult(int[] cards) {
|
||||||
if (!canSpin(false) || cards == null || cards.length == 0) {
|
if (!canSpin() || cards == null || cards.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,13 +56,15 @@ public class NewbieGachaState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Saves the pending result into the saved results list at the specified index or adds it
|
// Saves the pending result into the saved results list at the specified index or adds it
|
||||||
public boolean savePendingResult(int index) {
|
public boolean savePendingResult(Integer index) {
|
||||||
if (!hasPendingResult() || received) return false;
|
if (!hasPendingResult() || this.received) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (index >= 0 && index < savedResults.size()) {
|
if (index != null && index < savedResults.size()) {
|
||||||
// Replace existing slot
|
// Replace existing slot
|
||||||
savedResults.set(index, pendingResult);
|
savedResults.set(index, pendingResult);
|
||||||
} else if (savedResults.size() < saveCount) {
|
} else if (index == null && savedResults.size() < saveCount) {
|
||||||
// Add new slot if capacity allows
|
// Add new slot if capacity allows
|
||||||
savedResults.add(pendingResult);
|
savedResults.add(pendingResult);
|
||||||
} else {
|
} else {
|
||||||
@@ -75,19 +75,26 @@ public class NewbieGachaState {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a clone of the saved result at the given index
|
// Returns a clone of the result referenced by obtain request index.
|
||||||
|
// Idx=0 -> pendingResult, Idx=1-saveCount -> savedResults[idx-1]
|
||||||
public int[] copySavedResult(int index) {
|
public int[] copySavedResult(int index) {
|
||||||
return (index >= 0 && index < savedResults.size()) ? savedResults.get(index).clone() : null;
|
if (index == 0) {
|
||||||
|
return hasPendingResult() ? pendingResult.clone() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for claiming a specific result
|
int savedResultIndex = index - 1;
|
||||||
|
return (savedResultIndex >= 0 && savedResultIndex < savedResults.size()) ? savedResults.get(savedResultIndex).clone() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether the obtain request index points to a valid result.
|
||||||
|
// Idx=0 -> pendingResult, Idx=1-saveCount -> savedResults[idx-1]
|
||||||
public boolean canObtain(int index) {
|
public boolean canObtain(int index) {
|
||||||
return !received && index >= 0 && index < savedResults.size();
|
return !received && copySavedResult(index) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marks a specific saved result as claimed and closes the gacha session
|
// Marks the selected obtain request index as claimed and closes the gacha session.
|
||||||
public boolean markReceived(int index) {
|
public boolean markReceived(int index) {
|
||||||
if (received || index < 0 || index >= savedResults.size()) {
|
if (received || copySavedResult(index) == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ public class HandlerGachaNewbieObtainReq extends NetHandler {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
var req = GachaNewbieObtainReq.parseFrom(message);
|
var req = GachaNewbieObtainReq.parseFrom(message);
|
||||||
|
if (!req.hasId() || req.getId() < 0 || (req.hasIdx() && req.getIdx() < 0)) {
|
||||||
|
return session.encodeMsg(NetMsgId.gacha_newbie_obtain_failed_ack);
|
||||||
|
}
|
||||||
|
// req.getIdx() is optional, it always has a value
|
||||||
var change = Nebula.getGameContext().getGachaModule().obtainNewbie(session.getPlayer(), req.getId(), req.getIdx());
|
var change = Nebula.getGameContext().getGachaModule().obtainNewbie(session.getPlayer(), req.getId(), req.getIdx());
|
||||||
if (change == null) {
|
if (change == null) {
|
||||||
return session.encodeMsg(NetMsgId.gacha_newbie_obtain_failed_ack);
|
return session.encodeMsg(NetMsgId.gacha_newbie_obtain_failed_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!change.isEmpty()) {
|
|
||||||
session.getPlayer().addNextPackage(NetMsgId.items_change_notify, change.toProto());
|
|
||||||
}
|
|
||||||
|
|
||||||
return session.encodeMsg(NetMsgId.gacha_newbie_obtain_succeed_ack, change.toProto());
|
return session.encodeMsg(NetMsgId.gacha_newbie_obtain_succeed_ack, change.toProto());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,11 @@ public class HandlerGachaNewbieSaveReq extends NetHandler {
|
|||||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||||
var req = GachaNewbieSaveReq.parseFrom(message);
|
var req = GachaNewbieSaveReq.parseFrom(message);
|
||||||
Integer index = req.hasIdx() ? req.getIdx() : null;
|
Integer index = req.hasIdx() ? req.getIdx() : null;
|
||||||
boolean succeeded = Nebula.getGameContext().getGachaModule().saveNewbie(session.getPlayer(), req.getId(), index);
|
if (!req.hasId() || req.getId() < 0 || (index != null && index < 0)) {
|
||||||
if (!succeeded) {
|
return session.encodeMsg(NetMsgId.gacha_newbie_save_failed_ack);
|
||||||
|
}
|
||||||
|
boolean newbieSaveResult = Nebula.getGameContext().getGachaModule().saveNewbie(session.getPlayer(), req.getId(), index);
|
||||||
|
if (!newbieSaveResult) {
|
||||||
return session.encodeMsg(NetMsgId.gacha_newbie_save_failed_ack);
|
return session.encodeMsg(NetMsgId.gacha_newbie_save_failed_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user