@@ -244,6 +244,8 @@ public ItemStackResponse translateRequest(GeyserSession session, Inventory inven
244
244
245
245
PlayerInventory playerInv = session .getPlayerInventory ();
246
246
IntSet affectedSlots = new IntOpenHashSet ();
247
+
248
+ actionLoop :
247
249
for (ItemStackRequestAction action : request .getActions ()) {
248
250
switch (action .getType ()) {
249
251
case TAKE , PLACE -> {
@@ -260,15 +262,21 @@ public ItemStackResponse translateRequest(GeyserSession session, Inventory inven
260
262
int sourceSlot = bedrockSlotToJava (transferAction .getSource ());
261
263
GeyserItemStack sourceItem = inventory .getItem (sourceSlot );
262
264
if (playerInv .getCursor ().isEmpty ()) {
263
- playerInv .setCursor (sourceItem .copy (0 ), session );
265
+ // Bypass stack limit for empty cursor
266
+ playerInv .setCursor (sourceItem .copy (transferAmount ), session );
267
+ sourceItem .sub (transferAmount );
264
268
} else if (!InventoryUtils .canStack (sourceItem , playerInv .getCursor ())) {
265
269
return rejectRequest (request );
270
+ } else {
271
+ transferAmount = playerInv .getCursor ().add (transferAmount );
272
+ sourceItem .sub (transferAmount );
266
273
}
267
274
268
- playerInv .getCursor ().add (transferAmount );
269
- sourceItem .sub (transferAmount );
270
-
271
- affectedSlots .add (sourceSlot );
275
+ // Don't add to affectedSlots if nothing changed.
276
+ // Prevents sendCreativeAction from adjusting stack size.
277
+ if (transferAmount > 0 ) {
278
+ affectedSlots .add (sourceSlot );
279
+ }
272
280
} else if (isCursor (transferAction .getSource ())) {
273
281
int destSlot = bedrockSlotToJava (transferAction .getDestination ());
274
282
GeyserItemStack sourceItem = playerInv .getCursor ();
@@ -278,10 +286,11 @@ public ItemStackResponse translateRequest(GeyserSession session, Inventory inven
278
286
return rejectRequest (request );
279
287
}
280
288
281
- inventory .getItem (destSlot ).add (transferAmount );
282
- sourceItem .sub (transferAmount );
283
-
284
- affectedSlots .add (destSlot );
289
+ transferAmount = inventory .getItem (destSlot ).add (transferAmount );
290
+ if (transferAmount > 0 ) {
291
+ sourceItem .sub (transferAmount );
292
+ affectedSlots .add (destSlot );
293
+ }
285
294
} else {
286
295
int sourceSlot = bedrockSlotToJava (transferAction .getSource ());
287
296
int destSlot = bedrockSlotToJava (transferAction .getDestination ());
@@ -292,11 +301,17 @@ public ItemStackResponse translateRequest(GeyserSession session, Inventory inven
292
301
return rejectRequest (request );
293
302
}
294
303
295
- inventory .getItem (destSlot ).add (transferAmount );
296
- sourceItem .sub (transferAmount );
304
+ transferAmount = inventory .getItem (destSlot ).add (transferAmount );
305
+ if (transferAmount > 0 ) {
306
+ sourceItem .sub (transferAmount );
307
+ affectedSlots .add (sourceSlot );
308
+ affectedSlots .add (destSlot );
309
+ }
310
+ }
297
311
298
- affectedSlots .add (sourceSlot );
299
- affectedSlots .add (destSlot );
312
+ if (transferAction .getCount () != transferAmount ) {
313
+ this .refreshPending = true ; // Fixes visual bug with cursor
314
+ break actionLoop ; // Inventory is not what client expects right now
300
315
}
301
316
}
302
317
case SWAP -> {
@@ -361,10 +376,16 @@ public ItemStackResponse translateRequest(GeyserSession session, Inventory inven
361
376
return rejectRequest (request );
362
377
}
363
378
364
- ServerboundSetCreativeModeSlotPacket creativeDropPacket = new ServerboundSetCreativeModeSlotPacket ((short )-1 , sourceItem .getItemStack (dropAction .getCount ()));
379
+ int dropAmount = dropAction .getCount ();
380
+ if (dropAmount > sourceItem .maxStackSize ()) {
381
+ dropAmount = sourceItem .maxStackSize ();
382
+ sourceItem .setAmount (dropAmount );
383
+ }
384
+
385
+ ServerboundSetCreativeModeSlotPacket creativeDropPacket = new ServerboundSetCreativeModeSlotPacket ((short )-1 , sourceItem .getItemStack (dropAmount ));
365
386
session .sendDownstreamGamePacket (creativeDropPacket );
366
387
367
- sourceItem .sub (dropAction . getCount () );
388
+ sourceItem .sub (dropAmount );
368
389
}
369
390
case DESTROY -> {
370
391
// Only called when a creative client wants to destroy an item... I think - Camotoy
@@ -404,9 +425,12 @@ public ItemStackResponse translateRequest(GeyserSession session, Inventory inven
404
425
405
426
@ Override
406
427
protected ItemStackResponse translateCreativeRequest (GeyserSession session , Inventory inventory , ItemStackRequest request ) {
407
- ItemStack javaCreativeItem = null ;
428
+ GeyserItemStack javaCreativeItem = null ;
408
429
IntSet affectedSlots = new IntOpenHashSet ();
409
430
CraftState craftState = CraftState .START ;
431
+ boolean firstTransfer = true ;
432
+
433
+ actionLoop :
410
434
for (ItemStackRequestAction action : request .getActions ()) {
411
435
switch (action .getType ()) {
412
436
case CRAFT_CREATIVE : {
@@ -456,26 +480,39 @@ protected ItemStackResponse translateCreativeRequest(GeyserSession session, Inve
456
480
return rejectRequest (request );
457
481
}
458
482
483
+ int transferAmount = Math .min (transferAction .getCount (), javaCreativeItem .maxStackSize ());
459
484
if (isCursor (transferAction .getDestination ())) {
460
485
if (session .getPlayerInventory ().getCursor ().isEmpty ()) {
461
- GeyserItemStack newItemStack = GeyserItemStack .from (javaCreativeItem );
462
- newItemStack .setAmount (transferAction .getCount ());
486
+ GeyserItemStack newItemStack = javaCreativeItem .copy (transferAmount );
463
487
session .getPlayerInventory ().setCursor (newItemStack , session );
464
488
} else {
465
- session .getPlayerInventory ().getCursor ().add (transferAction . getCount () );
489
+ transferAmount = session .getPlayerInventory ().getCursor ().add (transferAmount );
466
490
}
467
491
//cursor is always included in response
468
492
} else {
469
493
int destSlot = bedrockSlotToJava (transferAction .getDestination ());
470
494
if (inventory .getItem (destSlot ).isEmpty ()) {
471
- GeyserItemStack newItemStack = GeyserItemStack .from (javaCreativeItem );
472
- newItemStack .setAmount (transferAction .getCount ());
495
+ GeyserItemStack newItemStack = javaCreativeItem .copy (transferAmount );
473
496
inventory .setItem (destSlot , newItemStack , session );
474
497
} else {
475
- inventory .getItem (destSlot ).add (transferAction .getCount ());
498
+ // If the player is shift clicking an item with a stack size greater than java edition,
499
+ // emulate the action ourselves instead.
500
+ if (firstTransfer && inventory .getItem (destSlot ).capacity () < transferAmount ) {
501
+ GeyserItemStack newItemStack = javaCreativeItem .copy (javaCreativeItem .maxStackSize ());
502
+ emulateCreativeQuickMove (session , inventory , affectedSlots , newItemStack );
503
+ this .refreshPending = true ;
504
+ break actionLoop ; // Ignore the rest of the client's actions
505
+ }
506
+
507
+ transferAmount = inventory .getItem (destSlot ).add (transferAmount );
476
508
}
477
509
affectedSlots .add (destSlot );
478
510
}
511
+
512
+ firstTransfer = false ;
513
+ if (transferAmount != transferAction .getCount ()) {
514
+ this .refreshPending = true ;
515
+ }
479
516
break ;
480
517
}
481
518
case DROP : {
@@ -489,14 +526,8 @@ protected ItemStackResponse translateCreativeRequest(GeyserSession session, Inve
489
526
return rejectRequest (request );
490
527
}
491
528
492
- ItemStack dropStack ;
493
- if (dropAction .getCount () == javaCreativeItem .getAmount ()) {
494
- dropStack = javaCreativeItem ;
495
- } else {
496
- // Specify custom count
497
- dropStack = new ItemStack (javaCreativeItem .getId (), dropAction .getCount (), javaCreativeItem .getDataComponents ());
498
- }
499
- ServerboundSetCreativeModeSlotPacket creativeDropPacket = new ServerboundSetCreativeModeSlotPacket ((short )-1 , dropStack );
529
+ ItemStack dropItem = javaCreativeItem .getItemStack (Math .min (dropAction .getCount (), javaCreativeItem .maxStackSize ()));
530
+ ServerboundSetCreativeModeSlotPacket creativeDropPacket = new ServerboundSetCreativeModeSlotPacket ((short )-1 , dropItem );
500
531
session .sendDownstreamGamePacket (creativeDropPacket );
501
532
break ;
502
533
}
@@ -513,14 +544,47 @@ protected ItemStackResponse translateCreativeRequest(GeyserSession session, Inve
513
544
return acceptRequest (request , makeContainerEntries (session , inventory , affectedSlots ));
514
545
}
515
546
516
- private static void sendCreativeAction (GeyserSession session , Inventory inventory , int slot ) {
547
+ private void sendCreativeAction (GeyserSession session , Inventory inventory , int slot ) {
517
548
GeyserItemStack item = inventory .getItem (slot );
549
+
550
+ // This does not match java client behaviour, but the java server will ignore creative actions with illegal stack sizes
551
+ if (item .getAmount () > item .maxStackSize ()) {
552
+ item .setAmount (item .maxStackSize ());
553
+ this .refreshPending = true ;
554
+ }
555
+
518
556
ItemStack itemStack = item .isEmpty () ? new ItemStack (-1 , 0 , null ) : item .getItemStack ();
519
557
520
558
ServerboundSetCreativeModeSlotPacket creativePacket = new ServerboundSetCreativeModeSlotPacket ((short )slot , itemStack );
521
559
session .sendDownstreamGamePacket (creativePacket );
522
560
}
523
561
562
+ private static void emulateCreativeQuickMove (GeyserSession session , Inventory inventory , IntSet affectedSlots , GeyserItemStack creativeItem ) {
563
+ int firstEmptySlot = -1 ; // Leftover stack is stored here
564
+
565
+ for (int i = 0 ; i < 36 ; i ++) {
566
+ int slot = i < 9 ? i + 36 : i ; // First iterate hotbar, then inventory
567
+ GeyserItemStack slotItem = inventory .getItem (slot );
568
+
569
+ if (firstEmptySlot == -1 && slotItem .isEmpty ()) {
570
+ firstEmptySlot = slot ;
571
+ }
572
+
573
+ if (InventoryUtils .canStack (slotItem , creativeItem ) && slotItem .capacity () > 0 ) {
574
+ creativeItem .sub (slotItem .add (creativeItem .getAmount ())); // Transfer as much as possible without passing stack capacity
575
+ affectedSlots .add (slot );
576
+ if (creativeItem .isEmpty ()) {
577
+ return ;
578
+ }
579
+ }
580
+ }
581
+
582
+ if (firstEmptySlot != -1 ) {
583
+ inventory .setItem (firstEmptySlot , creativeItem , session );
584
+ affectedSlots .add (firstEmptySlot );
585
+ }
586
+ }
587
+
524
588
private static boolean isCraftingGrid (ItemStackRequestSlotData slotInfoData ) {
525
589
return slotInfoData .getContainer () == ContainerSlotType .CRAFTING_INPUT ;
526
590
}
0 commit comments