LKShopMarket-0.8.1.jar
Curse Maven Snippet
What's new
LK Shop & Market v0.8.1
🇬🇧 English
🩹 A server crash you could hit without trying
The headline fix. With a full inventory, a stack on the cursor and a direct trade open, closing the screen crashed the server thread — no modified client required.
The trade screen blocks you from throwing items away while a trade is open. It did that by
cancelling the throw and putting the stack back with placeItemBackInInventory. But when there is
no room, that method drops the item instead — and Forge routes exactly that drop back through the
same event. The handler cancelled it and put the same stack back into the same full inventory,
forever: StackOverflowError.
Items now go back through a path that never drops, and whatever does not fit lands in your mailbox as a return. Returns deliberately ignore the mailbox cap, so this path can never run out of somewhere to put the item. The same change also fixes a quieter one: dying with a stack on the cursor mid-trade no longer destroys it.
💾 One corrupt entry no longer wipes a whole file
Three save files loaded their entries without a guard. A single unreadable entry threw, and what happened next was not a visible error but a silent total loss: the game caught it, handed back nothing, an empty file was built in its place, and the next autosave wrote that emptiness over the good one.
- Player stats — the worst of the three, because it holds the "already paid" flag for the welcome balance. An empty file meant every player on the server claimed the starting balance again, which is money created out of nothing. Frozen accounts and custom order limits went with it.
- Reputation — every seller score and every pending rating.
- Shops — every admin-built shop catalog.
Each entry is now isolated. A broken one is skipped and logged with its raw contents, which is the only thing an admin can rebuild it from — one lost instead of all of them.
🛡️ Hardening what travels over the network
- A malformed packet could take the client down. The wallet packet reserved its list using a size it had not checked; it was the only list decoder in the mod that skipped that validation. A server on a different version — or a hostile one — could announce two billion entries and the client ran out of memory before reading a single one. The size is now bounded, and checked against the bytes actually left in the packet.
- The outgoing byte budget always let one send through, however large, because it compared the quota before counting the send. It counts first now. One send still passes when the window is untouched, on purpose: without that, anything larger than the whole budget would never go out at all, and silently.
- A heavy mailbox turned every click into a burst. The wallet packet goes out after every action in the interface, and counting deliveries bounds the number of rows but not the size of the packet — a stack of cobblestone is five bytes, a full shulker box is thousands. There is now a 12 KB budget, actually measured rather than estimated, because the cost is almost all in the item's NBT and no guess from the item type gets close. At least one delivery always goes through, even if it exceeds the budget on its own.
🏪 Shops, orders and fees
- Closing time now applies while you are shopping. It was only checked when you clicked the villager, so opening the shop a minute before closing let you trade all night. The screen now closes when the hour arrives, and the server refuses the trade as well — that second check is the one that decides, because a packet can arrive in the same tick as the closing. It only applies to the shop you actually have open.
- The listing fee is refunded when the order shrinks. The fee is charged on the quantity you asked for, before anything is taken from your inventory. If fewer units were actually withdrawn, the difference stayed charged. It comes back now, and the corrected figure is what reaches the audit log and the undo window instead of a number nobody paid.
- "Publish" no longer lies about what you can afford. Selling did not check your balance at all and buying only checked the order's value, so the button looked ready and the order bounced with "you cannot afford the fee". Both directions now count the fee, using the same function the server charges with rather than a copy that could drift. And an operator exempt from fees is now told the fee is zero, instead of being shown one that is never charged.
📬 The mailbox
- Deliveries that existed but could not be seen. The mailbox sync was capped at the configured
mailbox size, on the reasoning that you can never hold more than fits. That reasoning is wrong:
returns deliberately ignore that cap, so a mailbox can pass it without doing anything unusual.
Everything beyond it was invisible in the interface even though "Claim All" handed it over
normally — which reads as lost items. The cap is gone, and the counters now show a
+when the list may be trimmed. - Items diverted to the mailbox now show up immediately. When a throw during a trade overflowed
into the mailbox, nothing told the client: the stack left your inventory with no sign of where it
went, and the mailbox tab kept showing the old contents until you reopened
/market. There is still no chat message — that is deliberate, so a modified client cannot spam one per attempt — but the mailbox refreshes silently.
🧹 Under the hood
- The market list stopped flickering empty. The two halves of the delta dictionary — the one the server keeps and the one the client keeps — did not shrink by the same rule. The server drops its half on every change of query; the client only dropped its own on reaching 512. In a long browsing session the client would let go of entries the server still referenced, and the list blanked for a round trip. The client's cache is now evicted one entry at a time by least-recent use, so nothing the server still refers to can age out.
- A shared stats sheet nobody could write to safely. Looking up a player with no record returned one single shared object, with public fields and no setters — "do not write to this" was a convention with nothing enforcing it. One stray write would have contaminated what every unknown player on the server sees, and the symptom (numbers appearing on brand-new accounts) does not point at its cause. It hands back a fresh one now.
- The formatted-rating cache had no ceiling. It gained an entry for every seller anyone ever looked at, including sellers with no votes at all, and nothing removed them. Capped at 512 by least-recent use.
🇪🇸 Español
🩹 Un cuelgue del servidor al que se llegaba sin buscarlo
El arreglo grande. Con el inventario lleno, un stack en el cursor y un trato directo abierto, cerrar la pantalla colgaba el hilo del servidor — sin necesidad de cliente modificado.
La pantalla del trato impide tirar items mientras dura. Lo hacía cancelando el tiro y reponiendo el
stack con placeItemBackInInventory. Pero cuando no queda hueco, ese método tira el item al
suelo — y Forge encamina justo ese tiro de vuelta al mismo evento. El manejador lo cancelaba y volvía
a reponer el mismo stack en el mismo inventario lleno, sin fondo: StackOverflowError.
Ahora se repone por un camino que no tira nada nunca, y lo que no cabe va al buzón como devolución. Las devoluciones se saltan el tope del buzón a propósito, así que este camino no puede quedarse sin sitio donde dejar el item. De paso arregla otro más callado: morir con un stack en el cursor a mitad de un trato ya no lo destruye.
💾 Una entrada corrupta ya no se lleva el fichero entero
Tres ficheros de guardado leían sus entradas sin guardia. Una sola ilegible reventaba, y lo que había detrás no era un error visible sino una pérdida total y callada: el juego capturaba la excepción, devolvía nada, se construía un fichero vacío en su lugar y el siguiente autoguardado escribía ese vacío encima del bueno.
- Estadísticas de jugador — el peor de los tres, porque guarda la marca de «ya cobrado» del saldo de bienvenida. Con el fichero en blanco, todo el servidor volvía a cobrarlo, o sea dinero creado de la nada. Se iban con él las cuentas congeladas y los límites de órdenes personalizados.
- Reputación — todas las notas de vendedor y todas las valoraciones pendientes.
- Tiendas — todos los catálogos de tienda montados por un administrador.
Ahora cada entrada va aislada. La rota se salta y se registra con su contenido crudo, que es lo único con lo que un administrador puede reponerla a mano: se pierde una en vez de todas.
🛡️ Blindaje de lo que viaja por la red
- Un paquete mal formado podía tirar el cliente. El paquete del monedero reservaba su lista con un tamaño que no había comprobado; era el único decodificador de lista del mod que se saltaba esa validación. Un servidor con otra versión —o uno hostil— anunciaba dos mil millones de entradas y el cliente se quedaba sin memoria antes de leer una sola. Ahora el tamaño está acotado, y además se contrasta con los bytes que de verdad quedan en el paquete.
- El presupuesto de salida dejaba pasar siempre un envío, midiera lo que midiera, porque comparaba el cupo antes de contar el envío. Ahora cuenta primero. Sigue pasando uno cuando la ventana está intacta, a propósito: sin esa salida, cualquier cosa mayor que el presupuesto entero no saldría nunca, y en silencio.
- Un buzón pesado convertía cada clic en una ráfaga. El paquete del monedero sale tras cada acción de la interfaz, y contar entregas acota el número de filas pero no el tamaño del paquete: un stack de adoquín ocupa cinco bytes y una caja shulker llena, miles. Ahora hay un presupuesto de 12 KB medido de verdad, no estimado, porque el coste está casi todo en el NBT del item y no hay suposición por tipo que se acerque. Siempre pasa al menos una entrega, aunque ella sola se pase.
🏪 Tiendas, órdenes y comisiones
- El horario de cierre ya se aplica mientras compras. Solo se comprobaba al hacer clic en el aldeano, así que abrir la tienda un minuto antes del cierre daba para comerciar toda la noche. Ahora la pantalla se cierra al llegar la hora, y además el servidor rechaza el trato — esa segunda comprobación es la que decide, porque un paquete puede entrar en el mismo tick que el cierre. Solo afecta a la tienda que tengas abierta de verdad.
- La comisión se devuelve cuando la orden encoge. Se cobra sobre la cantidad que pides, antes de tocar el inventario. Si al final se retiraban menos unidades, la diferencia se quedaba cobrada. Ahora vuelve, y la cifra corregida es la que llega a la auditoría y a la ventana de deshacer, en vez de un número que nadie pagó.
- «Publicar» ya no miente sobre lo que te puedes permitir. Al vender no se miraba el saldo en absoluto y al comprar solo el valor de la orden, así que el botón se veía listo y la orden rebotaba con «no te llega para la comisión». Ahora las dos direcciones la cuentan, usando la misma función con la que cobra el servidor en lugar de una copia que puede despegarse. Y a un operador exento se le dice que la comisión es cero, en vez de enseñarle una que nunca se le cobra.
📬 El buzón
- Entregas que existían y no se podían ver. La sincronización del buzón se recortaba al tamaño de
buzón configurado, con el razonamiento de que nunca puedes tener más de lo que cabe. Ese
razonamiento es falso: las devoluciones se saltan ese tope a propósito, así que un buzón puede
pasarlo sin hacer nada raro. Todo lo que sobrase era invisible en la interfaz aunque «Reclamar
Todo» lo entregara con normalidad — y eso se lee como items perdidos. El recorte se fue, y los
contadores llevan
+cuando la lista puede venir recortada. - Lo que se desvía al buzón aparece al momento. Cuando un tiro durante un trato desbordaba al
buzón, no se le decía nada al cliente: el stack se iba del inventario sin señal de dónde había ido,
y la pestaña del buzón seguía enseñando lo de antes hasta reabrir
/market. Sigue sin haber mensaje de chat —es a propósito, para que un cliente modificado no pueda generar uno por intento— pero el buzón se refresca en silencio.
🧹 Por dentro
- La lista del mercado dejó de parpadear en vacío. Las dos mitades del diccionario delta —la que guarda el servidor y la que guarda el cliente— no menguaban con la misma regla. El servidor tira la suya en cada cambio de consulta; el cliente solo tiraba la suya al llegar a 512. En una sesión larga de navegación, el cliente soltaba entradas que el servidor seguía referenciando y la lista se quedaba en blanco durante una ida y vuelta. Ahora la caché del cliente desaloja de una en una por uso menos reciente, así que nada a lo que el servidor siga apuntando puede caducar.
- Una ficha de estadísticas compartida en la que nadie podía escribir sin riesgo. Consultar a un jugador sin registro devolvía un único objeto compartido, con los campos públicos y sin setters: «no escribas aquí» era un convenio sin nada que lo hiciera cumplir. Una sola escritura despistada habría contaminado lo que ve todo jugador desconocido del servidor, y el síntoma (números que aparecen en cuentas recién estrenadas) no apunta a su causa por ningún lado. Ahora devuelve una ficha nueva.
- La caché de notas formateadas no tenía techo. Daba de alta una entrada por cada vendedor que alguien llegara a mirar, incluidos los que no tienen ni un voto, y nada las quitaba. Acotada a 512 por uso menos reciente.
Made by Nek4z.
This mod has no additional files

