Sto lavorando a un gioco multiplayer , in cui un giocatore ha molti depositi in cui archiviare gli oggetti.
archiviazione
Lo storage è il sistema che consente all'utente di depositare oggetti e riprenderli. Esempio di tipi di archiviazione:
- Spazio pubblicitario : come lo spazio pubblicitario di WoW, puoi aggiungere elementi, rilasciare oggetti, spostare oggetti dallo slot allo slot.
- Banca : il Bank è ciò che puoi chiamare, un super-inventario, che ha un limite di circa 300 articoli per scheda . La banca può avere un massimo di 10 schede che memorizzeranno gli articoli. gli elementi totali consentiti nella banca sono 3000 (10 schede moltiplicate per 300 voci per scheda) . Come mai, in banca, tutti gli articoli sono impilabili.
- Equipaggiamento : l'equipaggiamento è uguale all'inventario, solo con gli slot ordinati, cioè il timone, il corpo, le gambe e ci sono un totale di 8 slot.
- Petti e altro : come banco senza schede.
Un oggetto in gioco
Ogni elemento ha il proprio ID oggetto. Solo alcuni elementi possono essere impilabili, a meno che il tipo di archiviazione non sia Bank o chest.
Ho creato un sistema che mi faciliterà la gestione degli archivi, e c'è la classe astratta, che ogni classe di tipi di memoria la estenderà:
public abstract class PlayerItemStorage {
private int size;
/**
* The items
*/
private PlayerItem[] items;
/**
* Sometimes, by default, the storage items are id + 1, due to
* the client's interfaces. No exact explanation on it, but the only
* feature that doesn't do that is equipment and possible BoB
*/
private boolean increasedItem = true;
public PlayerItemStorage(int storageSize) {
this.items = new PlayerItem[storageSize];
this.size = storageSize;
}
public PlayerItemStorage(PlayerItem[] items) {
this.items = items;
this.size = items.length;
}
/**
* Resets the storage
*/
public void resetStorage() {
this.items = new PlayerItem[this.size];
}
/**
* Gets the item list
* @return PlayerItem list
*/
public PlayerItem[] getItems() {
return this.items;
}
/**
* Counts how many items there are with the same id.
* @param id Item id
* @return count
*/
public int count(int id) {
int count = 0;
for (PlayerItem item : this.items) {
if (this.getId(item) == id) {
count++;
}
}
return count;
}
/**
* Counts how many items there are in the inventory
* not including amount, just the item objects themself.
* @return amount
*/
public int countItems() {
int count = 0;
for (PlayerItem item : this.items) {
if (this.getId(item) > 0 && item.getAmount() > 0) {
count++;
}
}
return count;
}
/**
* Gets the length of the storage
* @return
*/
public int getLength() {
return this.items.length;
}
/**
* Checks how many free slots the storage has
* @return
*/
public int freeSlots() {
int slots = 0;
for (PlayerItem item : this.items) {
if (this.getId(item) <= 0) {
slots++;
}
}
return slots;
}
/**
* Finds an item id in the earliest slot.
* @param id Item ID
* @return Slot id
*/
public int find(int id) {
for (int i = 0; i < this.items.length; i++) {
PlayerItem item = this.items[i];
if ( this.getId(item) == id && item.getAmount() > 0) {
return i;
}
}
return -1;
}
/**
* Gets the earliest free slot
* @return slot id
*/
public int getFreeSlot() {
for (int i = 0; i < this.items.length; i++) {
if (this.getId(i) <= 0) {
return i;
}
}
return -1;
}
/**
* Checks if item is existing in the given slot, with the given amount and
* item id.
* @param id Item ID
* @param amount Item Amount
* @param slot Slot id
* @return boolean
*/
public boolean containsInSlot(int id, int amount, int slot) {
int found = 0;
if (this.items[slot].getAlpha() == id) {
for (int i = 0; i < this.items.length; i++) {
PlayerItem item = this.items[i];
if (this.getId(item) == id) {
if (item.getAmount() >= amount) {
return true;
} else {
found++;
}
}
}
if (found >= amount) {
return true;
}
return false;
}
return false;
}
/**
* Checks if item exists in the storage
*/
public boolean contains(int id) {
for (PlayerItem item : this.items) {
if (this.getId(item) == id) {
return true;
}
}
return false;
}
/**
* {@link #increasedItem}
* @param i Index
* @return real item id
*/
private int getId(int i) {
if (this.increasedItem) {
return this.items[i].getAlpha();
}
return this.items[i].getId();
}
/**
* {@link #increasedItem}
* @param item PlayerItem object
* @return real item id
*/
private int getId(PlayerItem item) {
if (this.increasedItem) {
return item.getAlpha();
}
return item.getId();
}
public abstract boolean add(int id, int amount);
public abstract boolean remove(int id, int amount, int fromSlot);
}
Ho anche una classe container che è responsabile del controllo di tutti gli archivi:
/**
* Container class for all storages of a player
* @author Ben
*/
public class PlayerStorageContainer {
/**
* Player object (Client extends Player extends Entity)
*/
public Player player;
/**
* boolCatach: If enabled, when removing/adding item to ALL of the storages,
* it will check if the action returned false, if yes it will stop right away and
* return false.
*/
private boolean boolCatch;
/**
* All of the storages the player will contain
*/
private Map<String, PlayerItemStorage> storages =
new HashMap<String, PlayerItemStorage>();
public PlayerStorageContainer(Player p) {
this.player = p;
this.initializePlayerStorages();
}
/**
* Initializes the existing storage for player.
*/
private void initializePlayerStorages() {
this.storages.put("inventory", new Inventory(this.player));
}
/**
* Gets the required PlayerItemStorage implementation
* @param name Name of the implementing object
* @return The object that extends PlayerItemStorage
*/
public PlayerItemStorage getStorage(String name) {
return this.storages.get(name);
}
/**
* Removes an item from ALL storages
* @param id The Item ID
* @param amount The amount of the item
* @return Returns a boolean if action was successful without any falses in the
* middle of the execution, will return always true if {!boolCatch}
*/
public boolean remove(int id, int amount) {
for (PlayerItemStorage storage : this.storages.values()) {
if (!storage.remove(id, amount) && this.boolCatch) {
return false;
}
}
return true;
}
/**
* Adds an item to ALL storages
* @param id The Item ID
* @param amount The amount of the item
* @return Returns a boolean if action was successful without any falses in the
* middle of the execution, will return always true if {!boolCatch}
*/
public boolean add(int id, int amount) {
for (PlayerItemStorage storage : this.storages.values()) {
if (!storage.add(id, amount) && this.boolCatch) {
return false;
}
}
return true;
}
/**
* Resets all storages
*/
public void reset() {
for (PlayerItemStorage storage : this.storages.values()) {
storage.resetStorage();
}
}
/**
* Toogles {@link PlayerItemContainer#boolCatch} for errors
* @param b
*/
public void setErrorCatch(boolean b) {
this.boolCatch = b;
}
}
Il problema
Questo sistema funziona bene per inventario e attrezzature - tuttavia, questo sarà un problema e causerà la duplicazione di un grosso codice algoritmico per il tipo di banca.
Poiché lo storage di tipo banca può avere più schede fino a 10, sarà necessario creare una classe denominata Tab e implementarla PlayerItemStorage e inserire lo stesso algoritmo nelle due classi (Bank e Tab) o disporre di un metodo in Bank , e poi avere una variabile denominata "focusedTab" che manterrà l'id della scheda selezionata, e rendere il metodo add accetta 3 argomentazioni (itemId, amount, tabId) .
Ma accettando 3 argomenti si creerà un problema, dovrò o ignorare il metodo astratto add, e creare il proprio metodo per esso, oppure creare 2 metodi di aggiunta astratta, uno con 2 parametri, uno con 3 che io trovare troppo disordinato.
Ho progettato questa cosa in modo errato che ho questi problemi? Come posso progettare una struttura adeguata per questa intera funzionalità di archiviazione in modo che funzioni con tutti i tipi di memorie a cui riesco a pensare?
Se non riesci a capire il problema, potresti trovare utile il mio thread di revisione del codice
Sfondo su come il server gestisce gli elementi
Quando il cliente fa clic su qualsiasi elemento, in qualsiasi interfaccia di gioco che supporti la memorizzazione degli elementi, invia l'ID dello slot su cui il giocatore ha fatto clic per motivi di sicurezza. Il server utilizza l'ID dello slot come indice dell'array PlayerItem [] per ottenere le informazioni sull'elemento.