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();
|
||||
}
|
||||
|
||||
public synchronized NewbieObtainLock lockNewbieObtain(int newbieId) {
|
||||
public synchronized boolean tryLockNewbieObtain(int newbieId) {
|
||||
if (this.lockedNewbieObtainIds.contains(newbieId)) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.lockedNewbieObtainIds.add(newbieId);
|
||||
return new NewbieObtainLockHandle(newbieId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean isNewbieObtainLocked(int newbieId) {
|
||||
return this.lockedNewbieObtainIds.contains(newbieId);
|
||||
}
|
||||
|
||||
private synchronized void unlockNewbieObtainInternal(int newbieId) {
|
||||
public synchronized void unlockNewbieObtain(int 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;
|
||||
|
||||
public class GachaModule extends GameContextModule {
|
||||
private final NewbieGachaModule newbieGachaModule = new NewbieGachaModule();
|
||||
|
||||
private final NewbieGachaModule newbieGachaModule;
|
||||
|
||||
public GachaModule(GameContext context) {
|
||||
super(context);
|
||||
this.newbieGachaModule = new NewbieGachaModule();
|
||||
}
|
||||
|
||||
public GachaResult spin(Player player, int bannerId, int amount) {
|
||||
|
||||
@@ -14,9 +14,6 @@ import java.util.List;
|
||||
|
||||
public final class NewbieGachaModule {
|
||||
|
||||
private record NewbieRequest(GachaManager manager, GachaNewbieDef newbieDef) {
|
||||
}
|
||||
|
||||
public List<GachaNewbieInfo> listInfos(Player player) {
|
||||
var newbieDefs = GameData.getGachaNewbieDataTable().values();
|
||||
var infos = new ArrayList<GachaNewbieInfo>(newbieDefs.size());
|
||||
@@ -54,21 +51,20 @@ public final class NewbieGachaModule {
|
||||
}
|
||||
|
||||
public int[] spin(Player player, int newbieId) {
|
||||
var request = resolveRequest(player, newbieId);
|
||||
if (request == null) {
|
||||
GachaNewbieDef newbieDef = GameData.getGachaNewbieDataTable().get(newbieId);
|
||||
if (newbieDef == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int newbieStateId = request.newbieDef().getId();
|
||||
int newbieStateId = newbieDef.getId();
|
||||
var bannerDef = GameData.getGachaDataTable().get(newbieStateId);
|
||||
if (bannerDef == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var manager = request.manager();
|
||||
synchronized (manager) {
|
||||
var state = loadStateForSpin(manager, request.newbieDef());
|
||||
if (state == null) {
|
||||
synchronized (player.getGachaManager()) {
|
||||
NewbieGachaState state = player.getGachaManager().getOrCreateNewbieState(newbieDef);
|
||||
if (player.getGachaManager().isNewbieObtainLocked(newbieStateId) || !state.canSpin()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -81,56 +77,52 @@ public final class NewbieGachaModule {
|
||||
return null;
|
||||
}
|
||||
|
||||
manager.saveNewbieState(state);
|
||||
player.getGachaManager().saveNewbieState(state);
|
||||
return cards;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean save(Player player, int newbieId, Integer index) {
|
||||
int resolvedIndex = index == null ? -1 : index;
|
||||
var request = resolveRequest(player, newbieId);
|
||||
if (request == null) {
|
||||
var newbieDef = GameData.getGachaNewbieDataTable().get(newbieId);
|
||||
if (newbieDef == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var manager = request.manager();
|
||||
synchronized (manager) {
|
||||
var state = loadStateForSave(manager, request.newbieDef());
|
||||
if (state == null) {
|
||||
synchronized (player.getGachaManager()) {
|
||||
if (player.getGachaManager().isNewbieObtainLocked(newbieId)) {
|
||||
return false;
|
||||
}
|
||||
var state = player.getGachaManager().getOrCreateNewbieState(newbieDef);
|
||||
if (state == null || !state.canSavePendingResult()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!state.savePendingResult(resolvedIndex)) {
|
||||
if (!state.savePendingResult(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
manager.saveNewbieState(state);
|
||||
player.getGachaManager().saveNewbieState(state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerChangeInfo obtain(Player player, int newbieId, int index) {
|
||||
if (index < 0) {
|
||||
GachaNewbieDef gachaNewbieDef = GameData.getGachaNewbieDataTable().get(newbieId);
|
||||
if (gachaNewbieDef == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var request = resolveRequest(player, newbieId);
|
||||
if (request == null) {
|
||||
int newbieStateId = gachaNewbieDef.getId();
|
||||
if (!player.getGachaManager().tryLockNewbieObtain(newbieStateId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int newbieStateId = request.newbieDef().getId();
|
||||
var obtainLock = request.manager().lockNewbieObtain(newbieStateId);
|
||||
if (obtainLock == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (var ignored = obtainLock) {
|
||||
try {
|
||||
NewbieGachaState state;
|
||||
int[] cards;
|
||||
synchronized (request.manager()) {
|
||||
state = loadStateForObtain(request.manager(), request.newbieDef(), index);
|
||||
if (state == null) {
|
||||
synchronized (player.getGachaManager()) {
|
||||
state = player.getGachaManager().findNewbieState(newbieStateId);
|
||||
if (state == null || !state.canObtain(index)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -147,66 +139,19 @@ public final class NewbieGachaModule {
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (request.manager()) {
|
||||
synchronized (player.getGachaManager()) {
|
||||
if (!state.markReceived(index)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
request.manager().saveNewbieState(state);
|
||||
player.getGachaManager().saveNewbieState(state);
|
||||
}
|
||||
|
||||
GachaRewardResolver.apply(player, rewardPlan, 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.
|
||||
public boolean canSpin(boolean obtainLocked) {
|
||||
return !received && !obtainLocked && remainingSpinCount > 0;
|
||||
public boolean canSpin() {
|
||||
return !received && remainingSpinCount > 0;
|
||||
}
|
||||
|
||||
// Checks if the current pending result can be moved to saved results
|
||||
public boolean canSavePendingResult(boolean obtainLocked) {
|
||||
return !received && !obtainLocked && hasPendingResult();
|
||||
public boolean canSavePendingResult() {
|
||||
return !received && hasPendingResult();
|
||||
}
|
||||
|
||||
// Updates the maximum allowed saved results.
|
||||
public boolean applyConfig(int saveCount) {
|
||||
int oldSaveCount = this.saveCount;
|
||||
public void applyConfig(int 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
|
||||
public boolean applySpinResult(int[] cards) {
|
||||
if (!canSpin(false) || cards == null || cards.length == 0) {
|
||||
if (!canSpin() || cards == null || cards.length == 0) {
|
||||
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
|
||||
public boolean savePendingResult(int index) {
|
||||
if (!hasPendingResult() || received) return false;
|
||||
public boolean savePendingResult(Integer index) {
|
||||
if (!hasPendingResult() || this.received) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index >= 0 && index < savedResults.size()) {
|
||||
if (index != null && index < savedResults.size()) {
|
||||
// Replace existing slot
|
||||
savedResults.set(index, pendingResult);
|
||||
} else if (savedResults.size() < saveCount) {
|
||||
} else if (index == null && savedResults.size() < saveCount) {
|
||||
// Add new slot if capacity allows
|
||||
savedResults.add(pendingResult);
|
||||
} else {
|
||||
@@ -75,19 +75,26 @@ public class NewbieGachaState {
|
||||
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) {
|
||||
return (index >= 0 && index < savedResults.size()) ? savedResults.get(index).clone() : null;
|
||||
if (index == 0) {
|
||||
return hasPendingResult() ? pendingResult.clone() : null;
|
||||
}
|
||||
|
||||
int savedResultIndex = index - 1;
|
||||
return (savedResultIndex >= 0 && savedResultIndex < savedResults.size()) ? savedResults.get(savedResultIndex).clone() : null;
|
||||
}
|
||||
|
||||
// Check for claiming a specific result
|
||||
// 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) {
|
||||
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) {
|
||||
if (received || index < 0 || index >= savedResults.size()) {
|
||||
if (received || copySavedResult(index) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,15 +13,15 @@ public class HandlerGachaNewbieObtainReq extends NetHandler {
|
||||
@Override
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
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());
|
||||
if (change == null) {
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,11 @@ public class HandlerGachaNewbieSaveReq extends NetHandler {
|
||||
public byte[] handle(GameSession session, byte[] message) throws Exception {
|
||||
var req = GachaNewbieSaveReq.parseFrom(message);
|
||||
Integer index = req.hasIdx() ? req.getIdx() : null;
|
||||
boolean succeeded = Nebula.getGameContext().getGachaModule().saveNewbie(session.getPlayer(), req.getId(), index);
|
||||
if (!succeeded) {
|
||||
if (!req.hasId() || req.getId() < 0 || (index != null && index < 0)) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user