Compare commits

...
251 Commits
Author SHA1 Message Date
5faed21218 Revert missing item count rendering changes from #1044 (#1091)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-10-26 00:21:23 +02:00
Worive aa9da2e0a0 Fix NEI initialization aborting while logging plugin errors (#1093) 2026-10-25 17:10:38 +02:00
slprime e9a3a494d5 Add Mouse Binding (#1092) 2026-10-25 02:49:38 +03:00
slprime ebe83f970f Fix group drag between bookmark namespaces (#1090) 2026-10-24 01:09:00 +03:00
slprime ad9beb7f90 Fill crafting grid with one recipe when Ctrl is held (#1089) 2026-10-23 23:44:16 +03:00
slprime 68bdb51c6b Update tree camera and scrollbar dragging every frame (#1087) 2026-10-22 23:58:41 +03:00
slprime e4e1964dec Fix NPE in BookmarksGridSlot when dragging a group across namespaces (#1086) 2026-10-22 16:22:23 +03:00
slprime 560618024f Autocrafting Optimization (#1084) 2026-10-21 01:24:40 +03:00
slprime e2a2d2a9df Batch 9-slice quads in DrawableResource (#1082) 2026-10-19 15:47:52 +03:00
slprime 7163630cb1 Move 9-slice setup into DrawableBuilder (#1081) 2026-10-16 23:18:28 +03:00
RessedandMartin Robertz ab168ad6ba Make StrictIdentifierFilter (<modid:name:meta>) resolve item on first match (#1080)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2026-10-16 20:42:59 +03:00
Martin Robertz e13e3483f0 update+deps+bs 2026-10-16 10:20:51 +02:00
slprime de26547b64 Treat item aliases as translation keys (#1079) 2026-10-15 20:19:58 +03:00
slprime c49f8808fd Fix NPE in item panel getMask (#1078) 2026-10-15 18:59:55 +03:00
slprime 1c6cc8b693 Unify hovered recipe lookup into HoveredRecipe (#1076) 2026-10-12 22:03:07 +03:00
slprime 43ae1b613a Add recipe origins to handler debug widget (#1072) 2026-10-12 13:43:22 +03:00
Wilhelm Schuster 402c4325a6 Update java version in GitHub issue template (#1074) 2026-10-11 23:33:54 +02:00
slprime 550e85c456 Fix -@mod and !@mod search syntax in Extended mode (#1071) 2026-10-10 14:58:17 +03:00
slprime 1673079cbb ItemZoom: Add side priority option (#1070) 2026-10-10 14:36:28 +03:00
slprime e6174c446a Add options for vertical line count in chain tooltips (#1068) 2026-10-09 21:14:11 +03:00
slprime 869b4571b0 Add recipe cycle freeze option (#1067) 2026-10-09 19:47:38 +03:00
slprime 35f5400ecb Fix Crafting Tree NPE (#1064) 2026-10-06 19:57:10 +03:00
micvog 1e2801c891 Fix crash when returning to the crafting tree after its bookmark group was removed (#1063) 2026-10-06 19:46:37 +03:00
Ressedandslprime 1b3b495fec Fix crash while dragging bookmark group to another grid (#1062)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-10-05 20:07:52 +03:00
slprime 2844ea35bc Fix Old Handlers Hack (#1061) 2026-10-04 22:07:41 +03:00
slprime c020ecbf1f Add Custom Renderer API for Fluids (#1059) 2026-10-02 01:22:58 +03:00
slprime a105a60a1a Fix Subset Width Calculation (#1058) 2026-10-01 23:16:35 +03:00
slprime 3a4a6734dc Fix NPE on GT fluid display without NBT (#1057) 2026-09-29 22:30:43 +03:00
slprime 11f9194de9 Fix EnderIo Tank (#1056) 2026-09-29 20:00:29 +03:00
slprimeandgithub-actions[bot] a685efb3b7 Recipe Group Optimization (#1054)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-28 00:19:29 +03:00
danyadevandMartin Robertz 9f3697bb67 fix cycling for fluid permutations (#1048)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2026-09-27 18:21:56 +03:00
slprime 18abb880f8 Add widget size to recipe event (#1053) 2026-09-27 14:13:27 +03:00
slprime 5868187410 Save Tree State (#1052) 2026-09-27 11:00:27 +03:00
slprime f84c4ef2bd Rename PositionStack.Fluid to Tank (#1051) 2026-09-27 08:09:52 +03:00
slprime a54c414595 Add Recipe Groups to GuiRecipe (#1049) 2026-09-27 07:39:27 +03:00
slprime 3c472a02cb Add Flowinf Texture to PositionStack (#1047) 2026-09-26 01:06:38 +03:00
slprime 382278bebb Fix Keybinding Category Names (#1046) 2026-09-25 23:36:57 +03:00
slprime 55c9811a2b Fix search autofocus losing the first typed character (#1045) 2026-09-25 22:17:38 +03:00
slprime 0ec87a8b9e Refactoring Chain Tooltip (#1044) 2026-09-25 22:08:15 +03:00
slprime 939845a9ed Add List View to Recipe (#1041) 2026-09-23 00:14:39 +03:00
Martin Robertz c9db437dd1 update+gradle+bs+deps+gitignore
fix curse auto uploader
2026-09-18 00:21:05 +02:00
Jakub b1f20104a6 Publishing (#1039) 2026-09-17 14:09:06 +02:00
AlexandAlex Vermette ab8d5f275f fix(NEIController): Skip MMB on slot click to avoid unecessary conflict with itemstack using it (#1038)
Co-authored-by: Alex Vermette <vermz@hotmail.ca>
2026-09-17 09:16:46 +03:00
slprime 9e87f2b158 Item Craftables Panel Optimization (#1036) 2026-09-14 18:36:58 +03:00
Dream Master ab512d3fd8 update deps 2026-09-14 10:28:02 +02:00
slprime 4a9f17590b Fix Slot Highlighting (#1035) 2026-09-14 08:44:35 +03:00
slprime a845e6c3e5 Registry NEI Fluids in Server (#1033) 2026-09-12 23:41:17 +03:00
Chitak 985 4f3a0826fe ItemFluidDisplay/getSubItems skips the last fluid (#1032) 2026-09-12 22:43:05 +03:00
slprime 6cd7697ca0 Repair Handler Optimization (#1031) 2026-09-12 20:02:52 +03:00
slprime 38dc14eca1 Fix concurrent modification crash in API.hideItem (#1030) 2026-09-11 08:30:49 +03:00
slprime 1fc824410b Change Fluid Amount Limit (#1029) 2026-09-11 08:10:28 +03:00
slprime af6dba1269 Correctly filtering neifluids (#1027) 2026-09-10 22:52:37 +03:00
slprime 2bf5a1cf9f Fix Bookmark Fluid Factor Limit (#1025) 2026-09-09 19:07:19 +03:00
Ranzuandgithub-actions[bot] 2126ec78bc Destroy one pixel and add color localization (#1022)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-09 13:34:25 +03:00
slprime 5f6209366a Fix Detect Fluid Display (#1021) 2026-09-09 07:06:14 +03:00
JarnexisandMykhailo Balaker f5fc4e704d Add Ukrainian (uk_UA) localization and translate README (#996)
Co-authored-by: Mykhailo Balaker <83900885+Angry3vilbot@users.noreply.github.com>
2026-09-08 18:52:48 +02:00
slprimeandgithub-actions[bot] 3d6c1c6e19 Implement Bookmark Tree Screen (#1018)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-08 13:56:58 +03:00
slprime fda7688807 Added fluid support in recipes (#1020) 2026-09-08 13:41:16 +03:00
boubou19andMartin Robertz 3f38e6f39b Various CPU and RAM improvements (#1015)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2026-09-03 00:07:25 +03:00
vcwdfca 866b796411 add centerOverlay (#1016) 2026-08-31 19:21:39 +03:00
slprime 7817640d96 Check unknown bookmark group (#1014) 2026-08-26 16:57:07 +03:00
vcwdfcaandslprime bd595f739c Add feature to save history like bookmarks (#1011)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-08-26 13:40:18 +03:00
slprime 4c74a01930 Fix crash when recipe search results change mid-rebuild (#1012) 2026-08-25 09:09:53 +03:00
slprime 6ced1e0604 Fix Fluid Untranslator (#1009) 2026-08-20 22:25:35 +03:00
slprime f8d00a5445 Add Accepts Label (#1007) 2026-08-14 22:41:15 +03:00
Taran Robertsandslprime 75d53b12ca New worlds ignoring global cheatmode (#1003)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-08-14 16:20:31 +03:00
slprime f93d5977ed Fix Autocrafting With GTTools (#1006) 2026-08-14 15:22:35 +03:00
slprimeandgithub-actions[bot] c4f9c54200 Fix Autocrafting Infinity Loop (#1004)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-14 13:27:42 +03:00
slprime 7d4c8b82ba Add custom render for PositionedStack (#1002) 2026-08-13 21:49:05 +03:00
slprime c6170f9795 Bookmarks: Fix Permutations with different amount (#1000) 2026-08-12 22:45:39 +03:00
Dream Master 5a533c8994 update+gradle+bs+deps 2026-08-11 00:26:14 +07:00
Nikolay S.andslprime d70a47a3a4 Modernize Java code (#997)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-08-10 14:55:40 +03:00
boubou19andgithub-actions[bot] 6c3db443d7 Cache enum .values() (#998)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-10 07:40:36 +02:00
boubou19 a51836fe9b Optimize updateUnlimitedItems (#995) 2026-08-07 19:05:01 +03:00
slprime 139ca22e51 Fix Untranslator With Broken Item (#993) 2026-08-04 19:43:34 +03:00
slprime 10fc78bdcf Add Recipe Filter syntax tooltip (#992) 2026-08-04 19:18:27 +03:00
slprime 5fb5ab6698 Added Bookmark Chain Visitor (#991) 2026-08-01 20:35:19 +03:00
slprime f47a8766a9 Create EmptyContainer (#990) 2026-08-01 20:19:54 +03:00
slprime 4cc72444d9 Fix Tooltip with Component in first line (#989) 2026-08-01 20:00:17 +03:00
slprime b48956ef94 Bookmarks Chain: fix container item multiplier limit (#988) 2026-07-29 00:57:12 +03:00
Algentandslprime 583d28ed12 Fix GL reset (#984)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-07-26 23:36:37 +03:00
koolkrafter5 384813347c Overlay Button moves one item if the recipe stack is size zero (#985) 2026-07-26 23:28:39 +03:00
slprime 2b28068382 Simplify the keybinding (#982) 2026-07-25 02:14:12 +03:00
slprime 2c204c25aa Rework chat item link to support bookmarking recipes and groups (#971) 2026-07-11 20:38:01 +03:00
Jordan Rey 5e6ff1d63b fix: spawner placement logic (#973) 2026-07-11 20:33:51 +03:00
Algent 53ce50b35a oregenPattern this time for sure (#974) 2026-07-11 19:53:43 +03:00
slprime 835c4f7e01 Fix Dynamic Keybinding (#972) 2026-07-11 14:20:03 +03:00
RecursivePineapple f8c1f39faf Add Cubic Chunks cube overlay & fix chunk grid above y=256, below y=0. (#970) 2026-07-10 11:02:43 +03:00
slprime 4c4716e64f Fix duplicate keybinding handling by patching KeyBinding static handlers (#968) 2026-07-08 09:19:48 +03:00
LunaandMartin Robertz c84ac27a18 Add offset for snow block to mob spawn overlay (#965)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2026-07-07 11:44:58 +02:00
mllfjn 47baee272c Remove redundant simplewasher recipe (#967) 2026-07-07 11:08:44 +02:00
Martin Robertz f55dc716e3 update deps 2026-07-04 19:50:07 +02:00
slprimeandMartin Robertz 26beb079f0 [Feature] Keybinding Refactoring (#957)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2026-07-04 14:38:02 +03:00
Algent 841b73c6fe Update way of getting oregen pattern for GT overlay (#964) 2026-06-30 18:18:32 +03:00
slprime 0c7eeebb1f Fix Debug Handler (#963) 2026-06-29 20:24:36 +03:00
slprime 65b56735a7 Add item name aliases for search (#961) 2026-06-29 18:08:59 +02:00
slprime ff428bf538 Allow plugins to force fill mode for overlay button (#960) 2026-06-28 13:11:27 -04:00
Martin Robertz a406021f26 deps 2026-06-27 15:15:43 +02:00
slprime 8500ac57eb Persist hidden items by GUID across reloads (#959) 2026-06-27 00:48:39 +03:00
slprime d97123ec13 ItemLoader Refactoring/Optimization (#958) 2026-06-26 21:25:07 +03:00
slprime 434931bfac Fix Loading Items error log (#956) 2026-06-21 00:57:41 +03:00
RanzuandMartin Robertz 860011036b Convert color class (#947)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2026-06-16 10:21:20 +02:00
slprime 937c1858a4 Fix Config Option NPE (#951) 2026-06-14 21:14:44 +03:00
slprime 4562ecf087 Fix Broken DisplayName in Untranslator (#949) 2026-06-13 17:46:46 +03:00
slprime d621d5d5bd Fix Autocrefing Permission (#948) 2026-06-13 17:25:07 +03:00
Martin Robertz 3175b316af update+gradle+deps+bs 2026-06-12 23:32:41 +02:00
slprime 61930ac7a8 Add server-controlled autocraft permission (#942) 2026-06-10 18:58:23 +03:00
slprime fcbf1dcade Fix Single Item In Bookmarks With Amount (#941) 2026-06-07 20:22:42 +03:00
slprime 3fa69e826c Guard loadTransferRects against plugin init errors (#939) 2026-05-27 08:26:29 +03:00
31fff21a55 Smart-Fallback & Alchemic Chemistry Set Fix (#935)
Co-authored-by: FFS <FFS@OhFuckOff>
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-05-27 02:29:59 +03:00
slprime 2e9ab88692 Fix ItemStack load from NBT with short overflow (#938) 2026-05-27 02:22:47 +03:00
slprime a02d293d44 Fix Recipe Page Area (#932) 2026-05-11 18:47:36 +03:00
sisyphussy 1265113390 Fix mui2 screen capture (#930) 2026-05-07 20:30:52 +03:00
slprime 4ffd614017 GuiRecipe: Add scrollPages Option (#929) 2026-05-07 18:57:15 +03:00
slprime 41554f4c5b Options: fix memory leaks (#928) 2026-05-06 22:36:07 +02:00
slprime 2104184077 GuiInfo: fix memory leaks (#927) 2026-05-06 20:18:35 +02:00
slprime d5f25a4be8 Prevent ConcurrentModificationException while searching for fuels in ItemList (#926) 2026-05-06 19:43:50 +03:00
slprime ccbf94790a Add Horizontal Scroll to Recipe GUI (#924) 2026-05-06 19:31:00 +03:00
slprime 2f4f72a2d6 Fix Filter Loading (#925) 2026-05-06 07:55:49 +03:00
danyadevandslprime f8cec9a78c Much faster recipe dumper (#920)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-04-25 14:58:50 +03:00
Lucaandslprime fb43a662eb Fix StringIndexOutOfBoundsException in FormattedTextField (#922)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-04-25 10:30:32 +00:00
Luca b2e0b8878a Fix bookmark recipe item order being inconsistent (#921) 2026-04-25 13:21:46 +03:00
slprime 374f019103 Fix Autocrafting NPE (#917) 2026-04-14 01:29:26 +03:00
slprime 5dfcff6f30 Rollback PR#913 (#916) 2026-04-13 20:39:15 +03:00
slprime bd17301cb9 Fix Collapsed Recipes in Bookmarks (#915) 2026-04-13 15:41:30 +03:00
lc-1337 96ccd4e109 fix client -> server sync in creative gui (#913) 2026-04-12 14:47:27 +03:00
Kogepan229 b30ca57e68 Don't block slot click if canCheatItem is false (#912) 2026-04-08 18:35:08 +03:00
slprime 8f949bc1d1 Refactor recipe profiler (#911) 2026-04-07 13:10:39 +03:00
slprime d25365942a Fix Repair Handler with broken ItemStack (#910) 2026-04-06 21:35:47 +03:00
slprime eb58e1dabc Improve Recipe Badge (#909) 2026-04-06 20:39:41 +03:00
slprime f4601e7eed Disable badge by default (#907) 2026-04-04 21:12:05 +03:00
slprime ba48806667 Chance tooltip (#906) 2026-04-04 00:03:15 +03:00
Martin Robertz 70c332796a update+deps 2026-04-03 16:34:57 +02:00
slprime bcc25d4369 Fix Collapsed Recipes in Bookmarks (#905) 2026-03-31 20:47:23 +03:00
slprime 10a3109714 Add recipe chance to bookmarks (#903) 2026-03-30 20:36:55 +03:00
koolkrafter5 b60769864f Improve image exporter data dumper (#902) 2026-03-30 08:19:34 +03:00
slprime 6bc899769f Add Reset to Debug Handler (#901) 2026-03-26 22:22:13 +02:00
slprime 0ba14091fb Refactor DebugHandlerWidget (#900) 2026-03-22 14:10:10 +02:00
slprimeandUltraProdigy 6ed0be7961 Add Repair Handler (#899)
Co-authored-by: UltraProdigy <187078471+UltraProdigy@users.noreply.github.com>
2026-03-21 15:04:09 +02:00
slprime d40b951cce Fix areStacksSameTypeWithNBT damage check (#896) 2026-03-19 14:43:45 +02:00
slprime ecdf7892ce updateBuildScript (#895) 2026-03-19 09:39:59 +02:00
slprime 1eaaac94bc Fix filtered permutations (#893) 2026-03-18 01:25:27 +02:00
slprime e99167dddb Change calculateBounds (#892) 2026-03-15 22:41:31 +02:00
slprime 34cb0dc43f Set minimum GTNHLib version to 0.9.6 (#891) 2026-03-15 16:07:31 +01:00
slprime acaad4bf91 Fix Favorite Option (#887) 2026-03-09 21:23:22 +02:00
slprime 3de75f4e26 Fix bookmark slot placement logic (#885) 2026-03-08 12:23:18 +02:00
slprime 47497090ea fix glColor4f; disabled generateSingleRecipeFavorites by default (#884) 2026-03-07 16:58:30 +02:00
slprime 8b45a1757f Add Favorite Item Support (#880) 2026-03-07 16:34:41 +02:00
UltraProdigy 2782f8252e Optimize images (#883) 2026-03-07 09:40:11 +01:00
slprime a5538b2566 Always Show Recipe Ingredients Overlay (#878) 2026-03-01 11:38:05 +02:00
slprime a1f324b796 Remove Generated Favorites from Accepts Followings Tooltip (#873) 2026-02-23 09:32:15 +02:00
slprimeandMartin Robertz f3b6be26df Generate Single-Recipe Favorites (#863)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2026-02-16 23:14:55 +01:00
slprime 9c764e3682 Restore the 'get infinite items' action (#867) 2026-02-16 18:39:17 +02:00
slprime 08605c3d73 Removed GTNHLib from required dependency (#866) 2026-02-16 04:37:20 -05:00
slprime 9ec1e0f0cd Change Bookmarks Panel Width (#862) 2026-02-10 19:41:28 +02:00
UltraProdigy e61151186c Exclude Duplicates (#860) 2026-02-09 23:05:15 +01:00
Martin Robertz f7787a9a22 update gradle + deps + bs 2026-02-09 22:32:25 +01:00
slprimeandUltraProdigy a91083bb5a Fix Pull Bookmarked Items (#857)
Co-authored-by: UltraProdigy <187078471+UltraProdigy@users.noreply.github.com>
2026-02-09 21:29:07 +00:00
slprimeandUltraProdigy 51f830bdf6 Reset item panel page on search result size change (#855)
Co-authored-by: UltraProdigy <187078471+UltraProdigy@users.noreply.github.com>
2026-02-09 16:18:22 -05:00
slprime 9326ac48ad Align panels from center to edges (#856) 2026-02-09 21:11:10 +00:00
c125b71ae0 Added checks for abstract classes when retrieving entities (#833)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-08 20:49:58 +00:00
Ranzu 805b90d6f8 Add debug text localization and tooltips (#854) 2026-02-03 17:58:20 +02:00
RanzuandMartin Robertz 900b9f90e7 Added show all handlers and recipes function (#852)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2026-02-03 15:38:35 +02:00
Ranzuandslprime 06d4a96c3a Add texture for toggled state of magnet button (#849)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-01-26 20:15:10 +02:00
sbwaz 9a5a4a77ef [Extended+] Quote in extended+ depending on "Quote Drop Item Name" NEI option (#848) 2026-01-26 20:08:27 +02:00
Connor-ColensoandConnor Colenso 4d874ec910 Format number centralisation (#846)
Co-authored-by: Connor Colenso <colen@CONNORSPC>
2026-01-23 20:51:38 -05:00
slprime 7f0f2466a5 Fix Information Handler Background Padding (#843) 2026-01-19 13:13:14 +02:00
slprime 83e1966ff2 Fix Recipe Handlers Padding (#842) 2026-01-19 07:47:05 +01:00
slprime 548f754823 Fix Autocrafting for recipe without ingredients in bookmarks (#841) 2026-01-19 07:46:46 +01:00
slprime e036fd14a8 Correctly crop colored text (#839) 2026-01-13 15:16:09 +02:00
slprime 463f4c9b60 Fix transparency border (#838) 2026-01-13 13:06:59 +02:00
Ranzu 5070fde640 Added optional subset label localization (#836) 2026-01-06 03:02:38 +02:00
454b4f7e1d Added Transparency Border to recipe-background and catalyst-tab (#822)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2025-12-28 15:40:21 +01:00
7b83456f01 Add tooltip for "Required Crafts" with shift (#810)
Co-authored-by: Wilhelm Schuster <dev@rot13.io>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2025-12-28 15:36:39 +02:00
PLASMAchicken 4b7b84b031 Restore NEIModContainer public plugins field (#829) 2025-12-26 19:31:41 +01:00
slprime 75309b81ec Hide Untranslator Subset when Untranslator disabled (#827) 2025-12-21 13:26:26 +02:00
slprimeandMartin Robertz 24e46bb43e Add the ability to move bookmark groups between namespaces (#803)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-12-16 23:39:32 +02:00
Alexdoru 2ba0773401 [Memory-opti:fix leak] Fix WorldServer leak in NEIServerConfig (#815) 2025-12-16 19:44:15 +00:00
PLASMAchicken eac913c80c Remove Obsolete Code from ModContainer (#820) 2025-12-13 12:42:40 +02:00
slprime 126f0ea73c Fix missing favorites and incorrect crafting chains for recipes with identical ingredients (#814) 2025-11-25 07:24:30 +01:00
slprimeandMartin Robertz cd4668ec84 Backup bookmarks.ini if loading failed (#798)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-11-24 08:52:01 +02:00
slprime 7c247dd0d2 Ignore space hotkeys when typing in search bar (#808) 2025-11-24 00:21:24 +02:00
slprime 87b549fab5 Normalize Fluid Packet (#811) 2025-11-16 14:17:56 +02:00
slprimeandMartin Robertz 8ed1f7a043 Properly clears inventory before filling a recipe (#796)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-11-13 08:19:55 +02:00
slprime f2efa35654 Extend HandlerInfo.Builder interface (#809) 2025-11-08 23:54:35 +02:00
slprime bcef287e71 Fix Permutations for Information Handler (#804) 2025-11-08 09:32:47 +02:00
slprime 7f555f7a7f Add Tooltip for debug option (#805) 2025-11-08 09:27:21 +02:00
slprime 9fd38da205 Correctly collapse recipe (#807) 2025-11-08 09:18:14 +02:00
slprime 2fa5563408 Optimizing cross-dimension loading (#806) 2025-11-08 09:12:40 +02:00
slprimeandMartin Robertz 541c0e2def Add 'Bookmark recipe with count' Option (#776)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-11-07 19:54:09 +02:00
Martin Robertz ac868097e7 update 2025-11-07 18:32:09 +01:00
slprimeandMartin Robertz 2f0de0b6a8 Add 'Show only favorites' option to the 'Craftables' panel (#800)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-11-06 17:37:00 +02:00
sisyphussy 32bc29e654 Remove glBlitFramebuffer (#801) 2025-11-06 14:36:30 +02:00
slprimeandMartin Robertz ef8a7d8bd5 Create normalizeRecipeQueryStack (#772)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-11-05 23:15:45 +02:00
slprimeandMartin Robertz adc5a5df16 Add GuiOverlapSlots for subpanels (#799)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-11-05 21:55:33 +02:00
slprime a828ff227a Remove try/catch from static block (#797) 2025-11-05 19:08:55 +01:00
slprime 9b6f98330e Remove Permutations from Output (#777) 2025-11-05 19:14:48 +02:00
slprime 667c8cb5b6 Implement Dynamic Handler Height (#792) 2025-11-05 19:00:09 +02:00
slprimeandMartin Robertz a186f69444 Fix favorite state refresh (#787)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-11-04 23:11:00 +01:00
slprime 16f4f20239 Fixed small mouse movement while double-clicking causing drop all items (#793) 2025-11-04 23:10:43 +01:00
slprime bac207cacb Change item slot size in GuiRecipe (#789) 2025-11-04 23:10:32 +01:00
slprime 630e856d83 Fix hotkeys when keyBinding disabled (#786) 2025-10-30 10:17:16 +02:00
slprime 42d96aed0c Show 'Delete Item' tooltip in Creative Mode (#785) 2025-10-29 21:47:34 +01:00
slprimeandsisyphussy bd6384bf24 Сalculate the height of the container before scrolling to the recipe (#782)
Co-authored-by: sisyphussy <221229741+sisyphussy@users.noreply.github.com>
2025-10-29 07:52:33 +00:00
slprime af9726d356 Fixed shift+scroll of ingredients in the recipe GUI (#783) 2025-10-29 07:42:18 +00:00
Martin Robertz 1e1d2f1076 update 2025-10-29 07:54:19 +01:00
slprimeandWilhelm Schuster 7cb94fb22a Clear rawText before formatting (#780)
Co-authored-by: Wilhelm Schuster <dev@rot13.io>
2025-10-25 15:20:28 +02:00
Martin Robertz 87aa4f9ed5 update 2025-10-25 14:18:58 +02:00
DarkShadow44andMartin Robertz 9760fd3eaf Fix another race condition, affecting IndustrialGrafter (#779)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-10-25 14:11:49 +02:00
slprime 1054871982 Add Scroll to Recipe Catalysts (#770) 2025-10-21 12:28:14 +00:00
slprime d90a667f68 Fix Bookmarks Chain Tooltip Height (#773) 2025-10-19 21:33:46 +03:00
slprime 69e6df5848 Fix Button Texture (#771) 2025-10-19 19:56:23 +02:00
slprime 25fa7ce44e Add defaultbookmarkcontainers.cfg (#769) 2025-10-19 16:07:45 +03:00
slprime e3cd7b860d Fix recipe result count (#767) 2025-10-18 11:07:39 +00:00
slprime 2a3aabc073 Add recipe index to recipe handler height (#768) 2025-10-18 13:03:08 +02:00
slprimeandMartin Robertz 76db43c835 Optimize permutations (#761)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-10-17 22:23:10 +03:00
slprimeandMartin Robertz 40e53eea7a Fixing HUD flickering (#762)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-10-17 21:36:30 +03:00
Martin Robertz 6c4d16d6b9 update 2025-10-17 12:50:52 +02:00
RealSilverMoon 4a2abe9fd7 Add a function to copy item name and meta (#758) 2025-10-17 10:54:47 +03:00
sisyphussy d47417302e Fix recipe tooltip lighting (#757) 2025-10-14 17:09:02 +03:00
e13401ded8 Optimize Bookmark Chains with Containers (#751)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: sisyphussy <alexrichter343@gmail.com>
2025-10-13 11:01:58 +00:00
sisyphussy 7ada842ef3 Fix tooltip item lighting (#755) 2025-10-13 00:29:09 +03:00
slprimeandMartin Robertz fe0c5d4e50 Create Recipe Widget; Recipe GUI: Infinity Scroll; Recipe GUI: Max Height Settings (#701)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-10-12 18:39:21 +02:00
slprime 2ea9ae1eb9 Show Handler Icon in Bookmarks (#747) 2025-10-09 21:22:37 +02:00
slprime f7f0596fda Show Items Loading Message (#745) 2025-10-08 16:58:23 +02:00
sbwaz d5e919a9cd [Extended+] Fix the prefixed sequence parsing. (#741) 2025-10-04 13:19:00 +02:00
sisyphussyandMartin Robertz a0ed5da699 Speedup rendering (#740)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-10-02 12:53:18 +02:00
Martin Robertz 99fb5a656e update 2025-10-02 12:13:08 +02:00
DarkShadow44 4b663222b1 Force tag compounds to be initialized so doesn't happen concurrently later (#730) 2025-10-02 07:15:28 +02:00
Martin Robertz 3e5d0088bc update 2025-10-01 17:21:16 +02:00
948be399d2 allow the steam furnace multiblock to show up in NEI under furnaces (#702)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: EnderProyects <45178400+EnderProyects@users.noreply.github.com>
2025-09-28 13:39:45 +02:00
DarkShadow44 5f6091924d Add more logging for fuel crash (#736) 2025-09-28 01:02:47 +03:00
Dredonkey c5bd1f897a Fix pressing '2' when hovering crafting output not putting the crafted item(s) in the hotbar (#733) 2025-09-27 10:17:40 +00:00
Julia 61d4b6ab62 Revert "Replace lambda with method reference" (#735) 2025-09-27 03:09:17 -07:00
Julia 791d05d5f5 Support Inline Comments After # (#734) 2025-09-26 18:11:47 -07:00
Mike_be 66001275c4 Remove UXV and MAX biolab from NEI recipe catalysts (#732) 2025-09-26 21:40:03 +02:00
slprime db6b83c906 Try fix Fuels (#729) 2025-09-24 13:17:34 +02:00
slprime 58ef6c85e0 Fix NBT Copy (#728) 2025-09-22 20:28:05 -07:00
Martin Robertz ab0aeef872 update 2025-09-21 21:38:41 +02:00
slprime 23b7c69e2f Fix None hotkeys for java8 (#726) 2025-09-21 19:44:20 +02:00
slprime bb89384493 Fix Bookmarks Shadow Items (#724) 2025-09-18 19:07:51 +02:00
slprime e96e4dfdde Create Item Untranslator (#719) 2025-09-17 12:42:35 +00:00
slprime 0a6a6771d8 Fixed Favorite Recipes in bookmarks for ingredients (#720) 2025-09-15 19:21:52 +02:00
sbwaz c96199a727 [Extended+] Filter permutations on the left. Fail on recipe expression, when not searching a recipe. (#716) 2025-09-13 11:18:29 +02:00
azod ab4ea2cfa1 feat(moboverlay): add chat message (on/off) (#715) 2025-09-11 03:45:06 -03:00
Mike_be b679883d02 Remove old entries which resulted in ghost catalysts (#714) 2025-09-08 20:11:32 +02:00
Worive 0048ef80d0 Fix(MobSpawnOverlay): avoid IOB from checking light value from block outside of world (#711) 2025-09-07 11:03:21 +00:00
slprime 84576aa7a2 Fix Permutation Filter (#712) 2025-09-06 22:32:14 +02:00
Dream Master c5cd5b9e61 update 2025-09-05 16:08:54 +02:00
slprimeandMartin Robertz b1bedcf403 Recipe search improvement (#706)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-09-04 15:59:51 +02:00
Matthew Bentham bba0a11466 Add missing lang key for favorite keybind (#709) 2025-09-04 13:20:53 +02:00
283 changed files with 23290 additions and 6489 deletions

No files matched your search

+2 -4
View File
@@ -33,12 +33,10 @@ body:
description: What Java version are you using? It's worth mentioning that if you play on Java9+ you should try update to latest minor release (e.g. prefer Java 17.0.6 over 17.0.2) of that version.
options:
- Java 8
- Java 9
- Java 11
- Java 17
- Java 19
- Java 20
- Java 21
- java 25
- java 26
- Other (Please Specify)
validations:
required: true
+17
View File
@@ -0,0 +1,17 @@
name: Optimize images
on:
pull_request:
branches: [ master, main, release/** ]
paths:
- "**/*.png"
push:
branches: [ master, main, release/** ]
paths:
- "**/*.png"
jobs:
optimize-images:
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master
secrets: inherit
+3 -1
View File
@@ -28,6 +28,7 @@ whitelist.json
*.iws
src/main/resources/mixins.*([!.]).json
*.bat
*.sh
*.DS_Store
!gradlew.bat
.factorypath
@@ -35,4 +36,5 @@ addon.local.gradle
addon.local.gradle.kts
addon.late.local.gradle
addon.late.local.gradle.kts
layout.json
layout.json
+2
View File
@@ -1,5 +1,7 @@
# NotEnoughItems Unofficial - By the GTNH Team
🌐 **Languages:** [English](README.md) | [Українська](README_uk.md)
A continuation of NotEnoughItems for 1.7.10 by the developers of Gregtech: New Horizons modpack, with features either inspired and/or backported from JustEnoughItems
We've tested this against all of the mods included in GTNH, as well as a limited set of other mods (like Reika's mods). Every effort has been made to maintain backwards compatibility, however the focus is on the mods contained in the GTNH modpack.
+93
View File
@@ -0,0 +1,93 @@
# NotEnoughItems Unofficial — від команди GTNH
🌐 **Мови:** [English](README.md) | [Українська](README_uk.md)
Продовження модифікації NotEnoughItems для 1.7.10 від розробників збірки GregTech: New Horizons із функціями, натхненними та/або бекпортованими з JustEnoughItems.
Ми протестували цю версію з усіма модами, що входять до GTNH, а також із обмеженим набором інших модів (як-от моди від Reika). Було докладено всіх зусиль для збереження зворотної сумісності, однак основну увагу приділено модам зі збірки GTNH.
Якщо у вас виникли проблеми з NEI за межами збірки GTNH, ви можете повідомити про них у [GitHub-репозиторії GTNH NEI](https://github.com/GTNewHorizons/NotEnoughItems).
## Нові можливості:
* Швидкодія
- Використовує паралельний потік для пошуку у списку предметів на кількох ядрах процесора, що в середньому пришвидшує пошук у 2–6 разів.
- Завантажує обробники рецептів паралельно.
* Текстове поле пошуку з більшістю очікуваних функцій — переміщення курсора вперед/назад, виділення тексту тощо.
* Закладки! Перебуваєте у процесі крафту? Додайте потрібний рецепт до закладок за допомогою клавіші «A» або налаштуйте власну клавішу.
* Перемикання видимості панелі закладок. Гаряча клавіша за замовчуванням — `B`. Меню «Підгрупи предметів» доступне лише тоді, коли панель закладок прихована.
* Кнопки утиліт та читів вирівнюються в ряд та переносяться на новий рядок залежно від розміру графічного інтерфейсу.
* Список предметів (`ItemList`) більше не генерується повторно з `ItemRegistry` під час кожного відкриття інвентарю.
* Вкладки у стилі JEI (або творчого режиму) [За бажанням]. Примітка: вимагає явної підтримки для рендерингу `ItemStack`, інакше використовуються перші дві літери назви обробника.
* Вкладки/обробники завантажуються з конфігураційного CSV-файлу в JAR-архіві (або за бажанням з папки `config`). Для авторів модів доступні IMC-повідомлення з NBT-даними `registerHandlerInfo` та `removeHandlerInfo` для додавання інформації про обробники з використанням тих самих полів, що й у CSV-файлі.
* Пошук за допомогою синтаксису `@[Мод]->[предмет]`. Наприклад: `@Mod.gregtech->iron ingot`.
* Перемикання між режимами рецептів, утиліт та читів за допомогою CTRL + ЛКМ по іконці гайкового ключа.
* Інструменти/предмети з GT5u та інструменти з GT6 тепер мають коректно працювати з функцією накладення та перенесення рецепта.
### Обробник інформаційних сторінок
Хочете додати інформацію про блок чи предмет, не створюючи для нього величезну підказку? Ви можете додати інформацію про будь-який блок чи предмет, зареєструвавши його в обробнику інформації.
Інформаційні сторінки відображаються під час пошуку рецептів або способів використання відповідного предмета. Усі відповідні предмети будуть циклічно змінюватися у верхній частині інформаційної сторінки.
Ваш мод може викликати:
```java
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", nbt);
```
Де `nbt` — це `NBTTagCompound`, відформатований, як описано нижче.
| Тег | Тип | Опис |
|------------------|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `filter` | `String` | Текстовий фільтр для визначення предметів, для яких має відображатися ця інформаційна сторінка.<br/>Можна використовувати `modname:itemname`, метадані, теги OreDictionary та регулярні вирази. Повний формат можна переглянути у `config/NEI/collapsibleitems.cfg`. |
| `page` / `pages` | `String` / `String NBTTagList` | Безпосередньо текст для відображення. Різницю між одно- та багатосторінковими повідомленнями див. нижче.<br/>Обидва формати підтримують автоматичний переклад наданих рядків. |
#### Односторінковий формат
Використовуйте текстовий тег `"page"`, якщо хочете додати лише одну сторінку інформації:
```java
NBTTagCompound tag = new NBTTagCompound();
tag.setString("filter", "minecraft:log 3");
tag.setString("page", "Exotic wood found in tropical climates.");
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", tag);
```
#### Багатосторінковий формат
Використовуйте тег `"pages"` як `NBTTagList` із рядків, якщо потрібно додати понад одну сторінку:
```java
NBTTagCompound tag = new NBTTagCompound();
tag.setString("filter", "minecraft:diamond_sword");
NBTTagList pages = new NBTTagList();
pages.appendTag(new NBTTagString("A powerful melee weapon."));
pages.appendTag(new NBTTagString("Can be enchanted for extra effects."));
tag.setTag("pages", pages);
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", tag);
```
* Кожен елемент списку — це одна сторінка.
* Сторінки з’являтимуться в NEI з кнопками для їх гортання.
#### Відповідність предметів
Усі предмети, що відповідають фільтру `filter`, відображатимуть вашу інформаційну сторінку. Повний формат фільтра див. у `config/NEI/collapsibleitems.cfg`.
#### Файл конфігурації інформації про предмети
Якщо ви творець збірки модів і хочете створити власні інформаційні сторінки для предметів, їх можна легко додати у файлі `config/NEI/informationpages.cfg`, де також наведено ще кілька прикладів.
## Інші примітки:
* Вилучено стиль TMI
* Вилучено збереження/завантаження стану інвентарю
## Ліцензія
Авторські права на модифікації від команди GTNH (c) 2019-2024 належать mitchej123 та команді GTNH.
Поширюється за ліцензією LGPL-3.0 або новішої версії — використовуйте цей код на власний розсуд, але, будь ласка, діліться з розробниками будь-якими внесеними вами змінами.
Окремі частини коду натхненні, запозичені або бекпортовані з [JEI](https://github.com/mezz/JustEnoughItems/tree/1.12) за ліцензією MIT.
Авторські права на оригінальний код (c) 2014-2015 належать mezz; код поширювався за ліцензією MIT.
+4 -1
View File
@@ -2,7 +2,6 @@ compileJava {
options.encoding = "UTF-8"
}
generateGrammarSource {
maxHeapSize = "128m"
arguments += ['-package', 'codechicken.nei.search', '-visitor', '-no-listener']
@@ -12,6 +11,10 @@ generateGrammarSource {
compileJava.dependsOn generateGrammarSource
sourcesJar.dependsOn generateGrammarSource
sourcesJar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
sourceSets {
main {
java.srcDirs += ["build/generated/sources/search/antlr/"]
+24 -2
View File
@@ -1,12 +1,17 @@
// Add your dependencies here
configurations.create('gtnhlibEmbed') {
canBeConsumed = false
canBeResolved = true
}
dependencies {
api('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
implementation('com.github.GTNewHorizons:GTNHLib:0.6.39:dev')
api('com.github.GTNewHorizons:CodeChickenCore:1.4.22:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.11.54:dev')
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
compileOnly('maven.modrinth:forge-essentials:7.5.1')
compileOnly('com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.14-alpha:dev')
antlr('org.antlr:antlr4:4.13.2')
shadowImplementation('org.antlr:antlr4-runtime:4.13.2')
@@ -17,4 +22,21 @@ dependencies {
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
compileOnly("com.github.GTNewHorizons:GTNHLib:0.11.54") { transitive = false }
gtnhlibEmbed("com.github.GTNewHorizons:GTNHLib:0.11.54") { transitive = false }
}
def embedArchiveOperations = gtnhGradle.archiveOperations
def gtnhlibEmbedContents = configurations.gtnhlibEmbed.elements.map { files ->
files.collect { embedArchiveOperations.zipTree(it.asFile) }
}
[tasks.jar, tasks.shadowJar].each {
it.configure {
from(gtnhlibEmbedContents) {
include 'com/gtnewhorizon/gtnhlib/color/ColorResource.class'
include 'com/gtnewhorizon/gtnhlib/color/ColorResource$*.class'
}
}
}
+44 -4
View File
@@ -39,9 +39,33 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = Developer
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# See https://github.com/bsideup/jabel for details on how this works.
enableModernJavaSyntax = true
# Enables modern Java syntax support. Valid values:
# - false: No modern syntax, Java 8 only
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
# - modern: Native modern Java bytecode, no downgrading
enableModernJavaSyntax = jabel
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
# and should *not* be used in most situations. -1 disables this.
# forceToolchainVersion = -1
# Target JVM version for JVM Downgrader bytecode downgrading.
# Only used when enableModernJavaSyntax = jvmDowngrader
# downgradeTargetVersion = 8
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
# Classes will be available in META-INF/versions/N/ for each version N in this list.
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
# jvmDowngraderMultiReleaseVersions = 21,25
# Specifies how JVM Downgrader API stubs are provided. Options:
# - shade: Shade minimized stubs into the jar
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
# - external: Another dependency provides stubs (no constraint, no warning)
# - (empty): Warning reminding you to configure stubs
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
# jvmDowngraderStubsProvider =
# Enables injecting missing generics into the decompiled source code for a better coding experience.
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
@@ -96,7 +120,9 @@ usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
# Specify the package that contains all of your Mixins. The package must exist or
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
# it must match with this or the build will fail.
mixinsPackage =
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
@@ -160,6 +186,11 @@ modrinthRelations = required-project\:codechickencore-unofficial
# Leave this empty if you don't want to publish on CurseForge.
curseForgeProjectId = 358228
# Comma-separated supported CurseForge environments: client, server (case-insensitive).
# Use client or server for mods that only support one environment. Defaults to client,server.
# These tags describe where the mod can run, not whether installation is required on both sides.
curseForgeEnvironments = client,server
# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge.
# Syntax: type1:name1;type2:name2;...
# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
@@ -172,6 +203,12 @@ curseForgeRelations = requiredDependency\:codechickencore-unofficial
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
@@ -201,3 +238,6 @@ curseForgeRelations = requiredDependency\:codechickencore-unofficial
# This is meant to be set in $HOME/.gradle/gradle.properties.
# ideaCheckSpotlessOnBuild = true
# Non-GTNH properties
org.gradle.configuration-cache = true
org.gradle.parallel = true
+12
View File
@@ -0,0 +1,12 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9064f22734b42ac03e85df04b632e167/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/4d0277c49991306ac78048580fe34bcf/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a9fa3b809e484a1e1ac4308a5dda661a/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/aa99913fa6d767d899ffc96082da01ca/redirect
toolchainVersion=25
Binary file not shown.
+3 -1
View File
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+5 -8
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+12 -24
View File
@@ -19,12 +19,12 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,30 +65,18 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.34'
}
+6 -4
View File
@@ -18,20 +18,22 @@ public SearchExpressionLexer(CharStream input, SearchTokenParser searchParser) {
// Lexer rules
REGEX_LEFT : 'r'? '/' -> pushMode(REGEX) ;
QUOTE_LEFT : '"' -> pushMode(QUOTED) ;
DASH : '-' ;
RECIPE_INGREDIENTS : '<&' | '<' ;
RECIPE_RESULT : '>&' | '>' ;
RECIPE_OTHERS : '^&' | '^' ;
PREFIX : CLEAN_SYMBOL {searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
OR : '|' ;
LEFT_BRACKET : '{' ;
RIGHT_BRACKET : '}' ;
PLAIN_TEXT : (CLEAN_SYMBOL | ESCAPED_SYMBOL)+ {!searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
NEWLINE_OR_TAB : [\t\r\n] -> skip ;
SPACE : ' ' ;
ESCAPED_SYMBOL : '\\' . ;
fragment ESCAPED_SYMBOL : '\\' . ;
fragment CLEAN_SYMBOL : ~[-<>^{}|/\\ "] ;
PREFIX : CLEAN_SYMBOL {searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
CLEAN_SYMBOL : ~[-<>^{}|/\\ "] ;
mode REGEX;
+4 -1
View File
@@ -56,7 +56,7 @@ prefixedExpression
token[Character prefix]
: regex[prefix]
| quoted[prefix]
| PLAIN_TEXT
| plainText
;
regex[Character prefix]
@@ -67,4 +67,7 @@ quoted[Character prefix]
: QUOTE_LEFT QUOTED_CONTENT QUOTE_RIGHT?
;
plainText
: (CLEAN_SYMBOL | ESCAPED_SYMBOL)+
;
@@ -5,13 +5,14 @@ import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.ShortcutInputHandler;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSlot, ? extends ItemsGrid.MouseContext>>
extends Widget implements IContainerTooltipHandler {
@@ -20,6 +21,7 @@ public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSl
protected int mouseDownItemIndex = -1;
protected int splittingLineColor = 0xffffffff;
protected int linePaddingStart = 0;
protected T grid;
public boolean isEmpty() {
@@ -52,19 +54,30 @@ public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSl
return this.grid.getSlotMouseOver(mousex, mousey);
}
protected void updateLinePadding() {
GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
this.linePaddingStart = 0;
while (GuiInfo
.hideItemPanelSlot(guiContainer, new Rectangle4i(this.x + this.linePaddingStart, this.y - 1, 10, 2))) {
this.linePaddingStart += 10;
}
}
protected void drawSplittingLine() {
float alpha = (this.splittingLineColor >> 24 & 255) / 255.0F;
float red = (this.splittingLineColor >> 16 & 255) / 255.0F;
float green = (this.splittingLineColor >> 8 & 255) / 255.0F;
float blue = (this.splittingLineColor & 255) / 255.0F;
int width = this.grid.columns * ItemsGrid.SLOT_SIZE;
int width = this.grid.columns * ItemsGrid.SLOT_SIZE - this.linePaddingStart;
int repeat = width / 6;
int shiftX = this.x + (this.w - width) / 2;
int shiftX = this.x + this.linePaddingStart + (this.w - width) / 2;
GL11.glColor4f(red, green, blue, alpha);
for (int i = 0; i < repeat; i++) {
DASH.draw(shiftX + i * 6, y);
DASH.draw(shiftX + i * 6, this.y);
}
GL11.glColor4f(1, 1, 1, 1);
}
@@ -122,13 +135,7 @@ public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSl
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|| !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) {
if (button == 0) {
GuiCraftingRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy());
} else if (button == 1) {
GuiUsageRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy());
}
ShortcutInputHandler.handleMouseClick(hoverSlot.getItemStack().copy());
} else {
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1);
}
@@ -7,8 +7,10 @@ import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.util.ChatAllowedCharacters;
import org.lwjgl.input.Keyboard;
@@ -30,6 +32,8 @@ public class AutoFocusWidget implements IContainerInputHandler {
protected boolean autofocus = false;
protected Point mouse;
protected Point pendingMouse;
protected GuiTextField textInputHolder;
public AutoFocusWidget() {
GuiContainerManager.addInputHandler(this);
@@ -38,32 +42,83 @@ public class AutoFocusWidget implements IContainerInputHandler {
public void load(GuiContainer gui) {
this.autofocus = LayoutManager.searchField.isVisible() && NEIClientConfig.searchWidgetAutofocus() != 0
&& isAllowedGuiAutoSearchFocus(gui);
this.mouse = getMousePosition();
this.mouse = null;
this.pendingMouse = null;
if (this.autofocus) {
beginTextInput();
}
}
protected void beginTextInput() {
if (this.textInputHolder == null) {
this.textInputHolder = new GuiTextField(Minecraft.getMinecraft().fontRenderer, 0, 0, 0, 0);
}
this.textInputHolder.setFocused(true);
}
protected void endTextInput() {
if (this.textInputHolder != null) {
this.textInputHolder.setFocused(false);
}
}
public void guiTick() {
if (!this.autofocus) {
return;
}
final Point current = getMousePosition();
if (this.mouse == null) {
if (current.equals(this.pendingMouse)) {
this.mouse = current;
}
this.pendingMouse = current;
} else if (!this.mouse.equals(current)) {
cancel();
}
}
protected void cancel() {
if (this.autofocus) {
this.autofocus = false;
endTextInput();
}
}
@Override
public boolean keyTyped(GuiContainer gui, char keyChar, int keyCode) {
if (this.autofocus) {
this.autofocus = false;
if (!this.mouse.equals(getMousePosition())) {
return false;
}
if (NEIClientConfig.searchWidgetAutofocus() == 2
&& GameSettings.isKeyDown(Minecraft.getMinecraft().gameSettings.keyBindInventory)) {
cancel();
return false;
}
if (keyCode == Keyboard.KEY_RETURN || keyCode == Keyboard.KEY_NUMPADENTER
|| keyCode == Keyboard.KEY_ESCAPE) {
cancel();
return false;
}
if (!ChatAllowedCharacters.isAllowedCharacter(keyChar)) {
return false;
}
this.autofocus = false;
LayoutManager.searchField.setFocus(true);
endTextInput();
final String oldText = LayoutManager.searchField.text();
LayoutManager.searchField.handleKeyPress(keyCode, keyChar);
return true;
return !LayoutManager.searchField.text().equals(oldText);
}
return false;
@@ -76,13 +131,13 @@ public class AutoFocusWidget implements IContainerInputHandler {
@Override
public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
this.autofocus = false;
cancel();
return false;
}
@Override
public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
this.autofocus = false;
cancel();
return false;
}
@@ -91,22 +146,22 @@ public class AutoFocusWidget implements IContainerInputHandler {
@Override
public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
this.autofocus = false;
cancel();
}
@Override
public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) {
this.autofocus = false;
cancel();
}
@Override
public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
this.autofocus = false;
cancel();
}
@Override
public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) {
this.autofocus = false;
cancel();
}
protected boolean isAllowedGuiAutoSearchFocus(GuiContainer gui) {
+124 -79
View File
@@ -19,6 +19,7 @@ import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import org.apache.commons.io.IOUtils;
@@ -29,6 +30,7 @@ import codechicken.nei.api.IBookmarkContainerHandler;
import codechicken.nei.bookmark.BookmarkGrid;
import codechicken.nei.bookmark.BookmarkGroup;
import codechicken.nei.bookmark.BookmarkItem;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.BookmarkStorage;
import codechicken.nei.bookmark.BookmarksGridSlot;
import codechicken.nei.bookmark.GroupingItem;
@@ -183,24 +185,33 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
if (itemStack != null && this.grid.indexOf(groupId, itemStack, recipeId, false) == -1) {
final boolean existsRecipe = recipeId != null
&& this.grid.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID);
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(itemStack);
long factor = nbTag.getInteger("Count");
final Recipe recipe = Recipe.of(recipeId);
final BookmarkItem.Builder builder;
if (recipe != null) {
builder = BookmarkItem.builder(groupId, itemStack, recipe, BookmarkItemType.RESULT).multiplier(1);
} else if (recipeId != null) {
builder = BookmarkItem.builder(groupId, itemStack).recipeId(recipeId)
.factor(StackInfo.getAmount(itemStack)).multiplier(1);
} else {
builder = BookmarkItem.builder(groupId, itemStack).factor(1).multiplier(StackInfo.getAmount(itemStack));
}
if (existsRecipe) {
builder.type(BookmarkItemType.RESULT);
for (int i = 0; i < this.grid.size(); i++) {
if (this.grid.getBookmarkItem(i).equalsRecipe(recipeId, groupId)) {
final long multiplier = this.grid.getBookmarkItem(i).getMultiplier();
factor *= multiplier;
builder.multiplier(this.grid.getBookmarkItem(i).getMultiplier());
break;
}
}
} else {
factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1;
builder.type(BookmarkItemType.ITEM);
}
this.grid.addItem(BookmarkItem.of(groupId, itemStack, factor, recipeId, false), true);
this.grid.addItem(builder.build(), true);
return true;
}
@@ -208,9 +219,11 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
public boolean addRecipe(Recipe recipe, int multiplier, int groupId) {
if (recipe == null) return false;
final RecipeId recipeId = recipe.getRecipeId();
if (recipe != null && this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) {
if (this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) {
this.grid.addRecipe(recipe, multiplier, groupId);
return true;
}
@@ -241,14 +254,22 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
ItemStack stack = recipeId.getResult();
if (stack != null && uniqueRecipe.add(recipeId)) {
this.grid.addItem(
BookmarkItem.of(groupId, stack, StackInfo.getAmount(stack), recipeId, false),
true);
final Recipe recipe = Recipe.of(recipeId);
final BookmarkItem.Builder builder;
if (recipe != null) {
builder = BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT);
} else {
builder = BookmarkItem.builder(groupId, stack).recipeId(recipeId)
.factor(StackInfo.getAmount(stack));
}
this.grid.addItem(builder.multiplier(1).type(BookmarkItemType.ITEM).build(), true);
uniqueStack.add(stack);
}
} else {
ItemStack stack = extractItem(item);
final ItemStack stack = extractItem(item);
if (stack != null && !uniqueStack.contains(stack)) {
this.grid.addItem(BookmarkItem.of(groupId, stack), true);
@@ -302,7 +323,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
final int groupId = slot.getGroupId();
final RecipeId recipeId = slot.getRecipeId();
final RecipeId recipeId = slot.getType() == BookmarkItemType.ITEM ? null : slot.getRecipeId();
final BookmarkGroup group = slot.getGroup();
if (group.crafting != null && group.collapsed) {
@@ -366,7 +387,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
public RecipeId getRecipeId(int itemIndex) {
BookmarkItem meta = this.grid.getCalculatedItem(itemIndex);
return meta.isIngredient ? null : meta.recipeId;
return meta.type == BookmarkItemType.INGREDIENT ? null : meta.recipeId;
}
public boolean existsRecipe(RecipeId recipeId, int groupId) {
@@ -504,31 +525,31 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
}
public Rectangle4i calculateBounds() {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
final int width = (gui.width - gui.xSize) / 2 - PADDING * 2;
final Rectangle4i bounds = new Rectangle4i(
PADDING,
PADDING,
(gui.width - 176) / 2 - PADDING * 2,
gui.height - PADDING * 2);
@Override
public Rectangle4i calculateBounds(GuiContainer gui) {
final int maxWidth = (gui.width - 176) / 2 - PADDING * 2;
final int maxHeight = gui.height - PADDING * 2;
final int freeSpace = (gui.width - gui.xSize) / 2 - PADDING * 2;
int paddingLeft = (int) Math
.ceil(bounds.w * NEIClientConfig.getSetting("world.panels.bookmarks.left").getIntValue() / 100000.0);
int paddingTop = (int) Math
.ceil(bounds.h * NEIClientConfig.getSetting("world.panels.bookmarks.top").getIntValue() / 100000.0);
int paddingRight = (int) Math
.ceil(bounds.w * NEIClientConfig.getSetting("world.panels.bookmarks.right").getIntValue() / 100000.0);
int paddingBottom = (int) Math
.ceil(bounds.h * NEIClientConfig.getSetting("world.panels.bookmarks.bottom").getIntValue() / 100000.0);
final int paddingLeft = (int) Math
.ceil(maxWidth * NEIClientConfig.getSetting("world.panels.bookmarks.left").getIntValue() / 100000.0);
final int paddingTop = (int) Math
.ceil(maxHeight * NEIClientConfig.getSetting("world.panels.bookmarks.top").getIntValue() / 100000.0);
final int paddingRight = (int) Math
.ceil(maxWidth * NEIClientConfig.getSetting("world.panels.bookmarks.right").getIntValue() / 100000.0);
final int paddingBottom = (int) Math
.ceil(maxHeight * NEIClientConfig.getSetting("world.panels.bookmarks.bottom").getIntValue() / 100000.0);
bounds.h = Math.max(ItemsGrid.SLOT_SIZE, bounds.h - paddingTop - paddingBottom);
bounds.y = bounds.y + Math.min(paddingTop, bounds.h - ItemsGrid.SLOT_SIZE);
final int width = Math.max(
ItemsGrid.SLOT_SIZE,
snapDownToMultiple(
Math.min(freeSpace - paddingLeft, maxWidth - paddingLeft - paddingRight)
- BookmarkGrid.GROUP_PANEL_WIDTH,
ItemsGrid.SLOT_SIZE))
+ BookmarkGrid.GROUP_PANEL_WIDTH;
final int height = Math.max(ItemsGrid.SLOT_SIZE, maxHeight - paddingTop - paddingBottom);
bounds.w = Math.max(ItemsGrid.SLOT_SIZE, Math.min(bounds.w - paddingLeft - paddingRight, width - paddingLeft));
bounds.x = bounds.x + Math.min(paddingLeft, bounds.w - ItemsGrid.SLOT_SIZE);
return bounds;
return new Rectangle4i(paddingLeft + PADDING, paddingTop + PADDING, width, height);
}
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
@@ -551,9 +572,10 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
return;
} else if (this.sortableGroup != null) {
final int overRowIndex = grid.getRowIndex(mousey);
final int overRowIndex = Math.min(grid.getRowIndex(mousey), grid.getRows() - 1);
if (overRowIndex >= 0 && this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) {
if (overRowIndex >= 0 && !isOverNamespaceControls(mousex, mousey)
&& !this.sortableGroup.matches(this.grid, this.grid.getRowGroupId(overRowIndex))) {
this.grid.moveGroup(this.sortableGroup, overRowIndex);
}
@@ -695,7 +717,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
@Override
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
public List<String> handleTooltip(int mousex, int mousey, List<String> currenttip) {
if (this.grid.getPerPage() == 0 || this.grid.isEmpty()) {
return new ArrayList<>();
@@ -722,7 +744,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
this.recipeChainTooltipLineHandler = null;
}
return super.handleTooltip(gui, mousex, mousey, currenttip);
return super.handleTooltip(mousex, mousey, currenttip);
}
@Override
@@ -748,7 +770,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
@Override
public Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> hotkeys) {
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
final int overRowIndex = this.grid.getHoveredRowIndex(true);
final BookmarksGridSlot slot = getSlotMouseOver(mousex, mousey);
@@ -778,15 +800,15 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB),
NEIClientUtils.translate("bookmark.group.toggle_crafting_chain"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.remove_recipe"),
KeyManager.getKeyName("bookmark.remove_recipe", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.remove_recipe"));
if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) {
if (BookmarkContainerInfo.getBookmarkContainerHandler(NEIClientUtils.getGuiContainer()) != null) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark_pull_items"),
KeyManager.getKeyName("bookmark.pull_items"),
NEIClientUtils.translate("bookmark.group.pull_items"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.pull_items", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.pull_items_shift"));
}
@@ -878,23 +900,25 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
}
return hotkeys;
return super.handleHotkeys(mousex, mousey, hotkeys);
}
private List<String> recipeChainTooltip(int groupId, List<String> currenttip) {
boolean crafting = this.grid.getGroup(groupId).crafting != null;
boolean crafting = this.grid.isCraftingMode(groupId);
if (this.recipeChainTooltipLineHandler == null || this.recipeChainTooltipLineHandler.groupId != groupId
|| this.recipeChainTooltipLineHandler.crafting != crafting) {
this.recipeChainTooltipLineHandler = new RecipeChainTooltipLineHandler(
groupId,
crafting,
this.grid.createRecipeChainMath(groupId));
final RecipeChainMath recipeChainMath = this.grid.createRecipeChainMath(groupId);
if (recipeChainMath != null) {
this.recipeChainTooltipLineHandler = new RecipeChainTooltipLineHandler(
groupId,
crafting,
recipeChainMath);
}
}
if (this.recipeChainTooltipLineHandler != null) {
currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.recipeChainTooltipLineHandler));
}
currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.recipeChainTooltipLineHandler));
return currenttip;
}
@@ -915,13 +939,24 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
@Override
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
List<String> currenttip) {
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> currenttip) {
final BookmarksGridSlot slot = this.grid.getSlotMouseOver(mousex, mousey);
if (slot != null) {
if (slot.isIngredient() && slot.getRecipeId() != null && slot.getBookmarkItem().permutations.size() > 1) {
if (slot.getBookmarkItem().chance != PositionedStack.CHANCE_FULL) {
final String chanceText = NEIClientUtils
.formatChance(slot.getBookmarkItem().chance / (float) PositionedStack.CHANCE_FULL);
currenttip.add(
1,
EnumChatFormatting.GRAY + NEIClientUtils.translate(
slot.getType() == BookmarkItemType.INGREDIENT ? "recipe.badge.chance.consume"
: "recipe.badge.chance.output",
chanceText));
}
if (slot.getType() == BookmarkItemType.INGREDIENT && slot.getRecipeId() != null
&& slot.getBookmarkItem().permutations.size() > 1) {
RecipeId recipeId = slot.getRecipeId();
if (recipeId == null) {
@@ -988,14 +1023,18 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
this.mouseDownSlot = -1;
}
@Override
public boolean onMouseWheel(int shift, int mousex, int mousey) {
if (!inEditingState() && new Rectangle4i(
private boolean isOverNamespaceControls(int mousex, int mousey) {
return new Rectangle4i(
namespacePrev.x,
namespacePrev.y,
namespaceNext.x + namespaceNext.w - namespacePrev.x,
namespacePrev.h).contains(mousex, mousey)) {
namespacePrev.h).contains(mousex, mousey);
}
@Override
public boolean onMouseWheel(int shift, int mousex, int mousey) {
if ((!inEditingState() || this.sortableGroup != null) && isOverNamespaceControls(mousex, mousey)) {
if (shift > 0) {
this.storage.prevNamespace();
@@ -1014,14 +1053,9 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
final BookmarksGridSlot slot = this.grid.getSlotMouseOver(mousex, mousey);
if (slot != null) {
int multiplier = 1;
if (NEIClientUtils.altKey()) {
multiplier = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
if (multiplier == 0) {
multiplier = slot.getItemStack().getMaxStackSize();
}
}
final long multiplier = NEIClientUtils.altKey()
? NEIClientUtils.getScrollMultiplier(slot.getItemStack())
: 1;
this.grid.shiftItemAmount(slot.itemIndex, multiplier * shift);
return true;
@@ -1063,20 +1097,23 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
activeStack = items.get((items.size() - shift + stackIndex) % items.size());
this.acceptsFollowingTooltipLineHandler.setActiveStack(activeStack);
this.grid.getBookmarkItem(Math.abs(itemIndex)).itemStack = activeStack;
this.grid.onItemsChanged();
final int targetItemIndex = Math.abs(itemIndex);
final BookmarkItem oldItem = this.grid.getBookmarkItem(targetItemIndex);
final BookmarkItem newItem = oldItem.copyWithPerm(activeStack);
this.grid.replaceBookmarkItem(targetItemIndex, newItem);
}
return true;
}
if (super.onMouseWheel(shift, mousex, mousey)) {
final int overRowIndex = grid.getRowIndex(mousey);
final int overRowIndex = Math.min(grid.getRowIndex(mousey), grid.getRows() - 1);
if (this.sortableGroup != null && overRowIndex >= 0
&& this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) {
&& !this.sortableGroup.matches(this.grid, this.grid.getRowGroupId(overRowIndex))) {
this.grid.moveGroup(this.sortableGroup, overRowIndex);
}
@@ -1113,7 +1150,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
for (BookmarkItem item : math.initialItems) {
final long invStackSize = inventory.getOrDefault(item.itemStack, 0L);
final long amount = item.amount - invStackSize * item.fluidCellAmount;
final long amount = item.getAmount() - invStackSize * item.fluidCellAmount;
if (amount > 0) {
pullItemStacks.add(item.getItemStack(amount));
}
@@ -1127,7 +1164,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
for (BookmarkItem item : math.initialItems) {
final long invStackSize = inventory.getOrDefault(item.itemStack, 0L);
final long strStackSize = containerStorage.getOrDefault(item.itemStack, 0L);
final long amount = item.amount - invStackSize * item.fluidCellAmount
final long amount = item.getAmount() - invStackSize * item.fluidCellAmount
- strStackSize * item.fluidCellAmount;
if (amount > 0) {
@@ -1181,7 +1218,15 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
}
if (!items.isEmpty()) {
containerHandler.pullBookmarkItemsFromContainer(guiContainer, items);
final int helmStackIndex = FastTransferManager.dropHeldItem(guiContainer);
if (guiContainer.mc.thePlayer.inventory.getItemStack() == null) {
containerHandler.pullBookmarkItemsFromContainer(guiContainer, items);
if (helmStackIndex != -1) {
FastTransferManager.clickSlot(guiContainer, helmStackIndex, 0, 0);
}
}
}
return true;
@@ -1252,8 +1297,8 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
@Deprecated
public BookmarkRecipeId getBookmarkRecipeId(int itemIndex) {
BookmarkItem item = this.grid.getCalculatedItem(itemIndex);
return item == null || item.isIngredient || item.recipeId == null ? null
: new BookmarkRecipeId(item.recipeId.getHandleName(), item.recipeId.getIngredients());
return item == null || item.type == BookmarkItemType.INGREDIENT || item.recipeId == null ? null
: new BookmarkRecipeId(item.recipeId.getHandlerName(), item.recipeId.getIngredients());
}
@Deprecated
@@ -57,6 +57,10 @@ public abstract class Button extends Widget {
return label;
}
public int getRenderLabelColor() {
return 0;
}
public final String label;
public Image icon;
@@ -1,10 +1,13 @@
package codechicken.nei;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -21,10 +24,13 @@ import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSelectWorld;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.resources.IResource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
@@ -37,11 +43,12 @@ import com.google.common.collect.Lists;
import codechicken.core.ClassDiscoverer;
import codechicken.core.ClientUtils;
import codechicken.core.GuiModListScroll;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.api.API;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.IConfigureNEI;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.bookmark.tree.GuiCraftingTree;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.StackInfo;
@@ -148,6 +155,7 @@ public class ClientHandler {
loadHiddenHandlers();
loadEnableAutoFocus();
loadGuiCraftablesBlacklist();
loadDefaultBookmarkContainers();
ItemInfo.preInit();
StackInfo.loadGuidFilters();
}
@@ -165,11 +173,34 @@ public class ClientHandler {
}, callback);
}
public static void loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
public static boolean loadSettingsResource(String resource, Consumer<Stream<String>> callback) {
final ResourceLocation location = new ResourceLocation("nei", resource);
try {
final IResource res = Minecraft.getMinecraft().getResourceManager().getResource(location);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(res.getInputStream(), StandardCharsets.UTF_8))) {
NEIClientConfig.logger.info("Loading '{}' from jar {}", resource, location);
callback.accept(
IOUtils.readLines(reader).stream()
.map(line -> line.contains("#") ? line.substring(0, line.indexOf('#')) : line)
.map(String::trim).filter(line -> !line.isEmpty()));
return true;
}
} catch (Exception io) {
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, location);
}
return false;
}
public static boolean loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
Consumer<Stream<String>> callback) {
File file = new File(NEIClientConfig.configDir, resource);
if (!file.exists()) {
if (!file.exists() && createDefault != null) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default '{}' {}", resource, file);
createDefault.accept(file, writer);
@@ -178,13 +209,20 @@ public class ClientHandler {
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
callback.accept(
IOUtils.readLines(reader).stream().filter(line -> !line.startsWith("#") && !line.trim().isEmpty()));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
if (file.exists()) {
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
callback.accept(
IOUtils.readLines(reader).stream()
.map(line -> line.contains("#") ? line.substring(0, line.indexOf('#')) : line)
.map(String::trim).filter(line -> !line.isEmpty()));
return true;
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
}
}
return false;
}
public static void loadSerialHandlers() {
@@ -218,6 +256,20 @@ public class ClientHandler {
lines -> ItemCraftablesPanel.guiBlacklist = lines.collect(Collectors.toList()));
}
public static void loadDefaultBookmarkContainers() {
loadSettingsFile("defaultbookmarkcontainers.cfg", lines -> {
for (String clazz : lines.collect(Collectors.toList())) {
try {
API.registerBookmarkContainerHandler(
(Class<? extends GuiContainer>) Class.forName(clazz),
new DefaultBookmarkContainerHandler());
} catch (Exception e) {
NEIClientConfig.logger.error("Failed to load class '{}' for defaultbookmarkcontainers.cfg", clazz);
}
}
});
}
public static void loadHiddenItems() {
loadSettingsFile("hiddenitems.cfg", lines -> lines.forEach(API::hideItem));
}
@@ -225,10 +277,10 @@ public class ClientHandler {
public static void load() {
instance = new ClientHandler();
GuiModListScroll.register("NotEnoughItems");
PacketCustom.assignHandler(NEICPH.channel, new NEICPH());
FMLCommonHandler.instance().bus().register(instance);
MinecraftForge.EVENT_BUS.register(instance);
MinecraftForge.EVENT_BUS.register(new GuiCraftingTree.EventHandler());
API.registerHighlightHandler(new DefaultHighlightHandler(), ItemInfo.Layout.HEADER);
HUDRenderer.load();
@@ -325,6 +377,7 @@ public class ClientHandler {
} else if (event.gui == null) {
/* prevent WorldClient reference being held in the Gui */
NEIController.manager = null;
GuiInfo.clearGuiHandlers();
}
}
@@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTBase.NBTPrimitive;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.StatCollector;
@@ -18,12 +19,12 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.EverythingItemFilter;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.PresetsList.Preset;
import codechicken.nei.PresetsList.PresetMode;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.filter.AnyMultiItemFilter;
import codechicken.nei.filter.EverythingItemFilter;
import codechicken.nei.filter.NothingItemFilter;
import codechicken.nei.util.ItemStackFilterParser;
public class CollapsibleItems {
@@ -112,7 +113,7 @@ public class CollapsibleItems {
group.setFilter(itemStr);
}
if (group != null && group.filter != null) {
if (group.filter != null) {
addGroup(group);
group = new GroupItem();
}
@@ -227,17 +228,17 @@ public class CollapsibleItems {
if (NEIClientConfig.world.nbt.hasKey(STATE_KEY)) {
NBTTagCompound states = NEIClientConfig.world.nbt.getCompoundTag(STATE_KEY);
@SuppressWarnings("unchecked")
final Map<String, NBTPrimitive> list = (Map<String, NBTPrimitive>) states.tagMap;
final Map<String, NBTBase> list = states.tagMap;
final Map<String, GroupItem> mapping = new HashMap<>();
for (GroupItem group : CollapsibleItems.groups) {
mapping.put(group.guid, group);
}
for (Map.Entry<String, NBTPrimitive> nbtEntry : list.entrySet()) {
for (Map.Entry<String, NBTBase> nbtEntry : list.entrySet()) {
if (mapping.containsKey(nbtEntry.getKey())) {
mapping.get(nbtEntry.getKey()).expanded = nbtEntry.getValue().func_150290_f() == 1;
mapping.get(nbtEntry.getKey()).expanded = ((NBTPrimitive) nbtEntry.getValue()).func_150290_f()
== 1;
}
}
}
@@ -0,0 +1,24 @@
package codechicken.nei;
import com.gtnewhorizon.gtnhlib.color.ColorResource;
public class ColorUtils {
private static final ColorResource.Factory color = new ColorResource.Factory("nei");
public static final ColorResource
// spotless:off
textGray = color.rgb("textGray", "0x404040"),
textLightGray = color.rgb("textLightGray", "0x606060"),
recipeTitle = color.rgb("recipeTitle", "0xFFFFFF"),
recipeTitleHover = color.rgb("recipeTitleHover", "0xFFFF55"),
subsetWidget = color.rgb("subsetWidget", "0xAA00AA"),
recipeBadge = color.rgb("recipeBadge", "0xFDD835"),
buttonLabelNormal = color.rgb("buttonLabelNormal", "0xE0E0E0"),
buttonLabelHover = color.rgb("buttonLabelHover", "0xFFFFA0"),
buttonLabelDisabled = color.rgb("buttonLabelDisabled", "0x601010"),
buttonLabelInactive = color.rgb("buttonLabelInactive", "0xA0A0A0"),
craftingTreeLine = color.argb("craftingTreeLine", "0xFF888888");
// spotless:on
}
@@ -0,0 +1,101 @@
package codechicken.nei;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.gui.GuiDraw.ITooltipLineHandler;
public abstract class CompositeTooltipLineHandler implements ITooltipLineHandler {
protected static final int SCREEN_MARGIN = 8 * 2 + 10;
protected static final int RESERVED_HEIGHT = 40;
protected final List<ItemsTooltipLineHandler> lines = new ArrayList<>();
protected final Dimension size = new Dimension();
protected boolean created = false;
protected int screenHeight = 0;
protected abstract void createLines();
protected boolean needsUpdate() {
return false;
}
@Override
public Dimension getSize() {
ensureCreated();
return this.size;
}
@Override
public void draw(int x, int y) {
if (this.size.height == 0) return;
for (ItemsTooltipLineHandler line : this.lines) {
line.draw(x, y);
y += line.getSize().height;
}
}
protected void ensureCreated() {
boolean update = needsUpdate();
update = this.screenHeight != (this.screenHeight = GuiDraw.displaySize().height) || update;
if (this.created && !update) {
return;
}
this.created = true;
this.lines.clear();
this.size.setSize(0, 0);
createLines();
fitToScreen();
for (ItemsTooltipLineHandler line : this.lines) {
this.size.width = Math.max(this.size.width, line.getSize().width);
this.size.height += line.getSize().height;
}
}
protected void fitToScreen() {
if (this.lines.isEmpty()) {
return;
}
final List<ItemsTooltipLineHandler> ordered = new ArrayList<>(this.lines);
ordered.sort(Comparator.comparingInt(ItemsTooltipLineHandler::getRows));
int budget = availableHeight() / ItemsTooltipLineHandler.SLOT_SIZE;
int rest = ordered.size();
for (ItemsTooltipLineHandler line : ordered) {
final int fair = Math.max(1, budget / rest);
if (line.getRows() > fair) {
line.setMaxRows(fair);
}
budget -= line.getRows();
rest--;
}
}
protected int availableHeight() {
return GuiDraw.displaySize().height - SCREEN_MARGIN
- RESERVED_HEIGHT
- this.lines.size() * ItemsTooltipLineHandler.headerHeight();
}
protected static int maxLineRows() {
final int height = GuiDraw.displaySize().height - SCREEN_MARGIN
- RESERVED_HEIGHT
- ItemsTooltipLineHandler.headerHeight();
return Math.max(1, height / ItemsTooltipLineHandler.SLOT_SIZE);
}
}
@@ -79,14 +79,13 @@ public class ContainerCreativeInv extends Container {
@Override
public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int slotIndex) {
ItemStack transferredStack = null;
Slot slot = (Slot) inventorySlots.get(slotIndex);
Slot slot = inventorySlots.get(slotIndex);
if (slot != null && slot.getHasStack()) {
ItemStack stack = slot.getStack();
transferredStack = stack.copy();
if (stack.getItem() instanceof ItemArmor) {
ItemArmor armor = (ItemArmor) stack.getItem();
if (stack.getItem() instanceof ItemArmor armor) {
if (!getSlot(90 + armor.armorType).getHasStack()) {
getSlot(90 + armor.armorType).putStack(transferredStack);
slot.putStack(null);
@@ -8,7 +8,6 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.ContainerEnchantment;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagList;
@@ -164,14 +163,14 @@ public class ContainerEnchantmentModifier extends ContainerEnchantment {
public boolean addEnchantment(int e, int level) {
if (e < Enchantment.enchantmentsList.length && Enchantment.enchantmentsList[e] != null) {
((Slot) inventorySlots.get(0)).getStack().addEnchantment(Enchantment.enchantmentsList[e], level);
inventorySlots.get(0).getStack().addEnchantment(Enchantment.enchantmentsList[e], level);
return true;
}
return false;
}
public void removeEnchantment(int e) {
ItemStack stack = ((Slot) inventorySlots.get(0)).getStack();
ItemStack stack = inventorySlots.get(0).getStack();
NBTTagList nbttaglist = stack.getEnchantmentTagList();
if (nbttaglist != null) {
for (int i = 0; i < nbttaglist.tagCount(); i++) {
@@ -1,9 +1,10 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import codechicken.nei.api.IBookmarkContainerHandler;
@@ -12,36 +13,18 @@ public class DefaultBookmarkContainerHandler implements IBookmarkContainerHandle
@Override
public void pullBookmarkItemsFromContainer(GuiContainer guiContainer, ArrayList<ItemStack> bookmarkItems) {
FastTransferManager manager = new FastTransferManager();
LinkedList<ItemStack> containerStacks = manager.saveContainer(guiContainer.inventorySlots);
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
final FastTransferManager manager = new FastTransferManager();
for (ItemStack bookmarkItem : bookmarkItems) {
int bookmarkSizeBackup = bookmarkItem.stackSize;
for (int i = 0; i < containerStacks.size() - 4 * 9; i++) { // Last 36 slots are player inventory
ItemStack containerItem = containerStacks.get(i);
if (containerItem == null) {
continue;
}
if (bookmarkItem.isItemEqual(containerItem)) {
if (bookmarkItem.stackSize <= 0) {
break;
}
int transferAmount = Math.min(bookmarkItem.stackSize, containerItem.stackSize);
for (int i = 0; i < slots.size() - 4 * 9; i++) {
final Slot slot = slots.get(i);
if (slot.getHasStack() && bookmarkItem.isItemEqual(slot.getStack())) {
final int transferAmount = Math.min(bookmarkItem.stackSize, slot.getStack().stackSize);
manager.transferItems(guiContainer, i, transferAmount);
bookmarkItem.stackSize -= transferAmount;
if (bookmarkItem.stackSize == 0) {
break;
}
}
}
bookmarkItem.stackSize = bookmarkSizeBackup;
}
}
}
@@ -20,7 +20,7 @@ public class DefaultHighlightHandler implements IHighlightHandler {
String name = null;
try {
String s = GuiContainerManager.itemDisplayNameShort(stack);
if (s != null && !s.endsWith("Unnamed")) name = s;
if (!s.endsWith("Unnamed")) name = s;
if (name != null) currenttip.add(name);
} catch (Exception ignored) {}
@@ -9,7 +9,9 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
@@ -89,7 +91,7 @@ public class FastTransferManager {
public static void clearSlots(Container container) {
for (int slotNo = 0; slotNo < container.inventorySlots.size(); slotNo++)
((Slot) container.inventorySlots.get(slotNo)).putStack(null);
container.inventorySlots.get(slotNo).putStack(null);
}
public void performMassTransfer(GuiContainer window, int fromSlot, int toSlot, ItemStack heldStack) {
@@ -281,6 +283,27 @@ public class FastTransferManager {
GuiContainerManager.getManager(window).handleSlotClick(slotIndex, button, modifier);
}
public static int dropHeldItem(GuiContainer window) {
final EntityPlayerSP player = window.mc.thePlayer;
final ItemStack held = player.inventory.getItemStack();
if (held == null) return -1;
for (int i = 0; i < window.inventorySlots.inventorySlots.size(); i++) {
final Slot slot = window.inventorySlots.inventorySlots.get(i);
if (slot.inventory instanceof InventoryPlayer && !slot.getHasStack() && slot.isItemValid(held)) {
clickSlot(window, i, 0, 0);
if (player.inventory.getItemStack() == null) {
return i;
}
}
}
return -1;
}
private boolean fillZoneWithHeldItem(GuiContainer window, int zoneIndex) {
for (int transferTo : slotZones.get(zoneIndex)) {
ItemStack held = NEIClientUtils.getHeldItem();
@@ -8,39 +8,144 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Objects;
import java.util.Set;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import org.apache.commons.io.IOUtils;
import com.google.common.io.Files;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import codechicken.core.CommonUtils;
import codechicken.nei.api.API;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.RecipeHandlerRef;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.FavoriteStorage;
import codechicken.nei.util.NBTJson;
public class FavoriteRecipes {
private static final Set<NBTTagCompound> tools = new HashSet<>();
private static final Map<NBTTagCompound, RecipeId> items = new HashMap<>();
private static final Map<String, RecipeId> fluids = new HashMap<>();
protected static final class FastKey {
public final ItemStack stack;
private int hash;
public FastKey(ItemStack stack) {
this.stack = stack;
this.hash = 31 + Item.getIdFromItem(stack.getItem());
this.hash = 31 * this.hash + stack.getItemDamage();
}
@Override
public int hashCode() {
return this.hash;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof FastKey k && this.stack.getItem() == k.stack.getItem()
&& this.stack.getItemDamage() == k.stack.getItemDamage()) {
return this.stack == k.stack || Objects.equals(this.stack.stackTagCompound, k.stack.stackTagCompound);
}
return false;
}
}
private static final RestartableTask singleRecipeFavoritesTask = new RestartableTask(
"Generate Single-Recipe Favorites") {
@Override
public void execute() {
final List<ICraftingHandler> handlers = GuiCraftingRecipe.getCraftingHandlers("all");
final Map<FastKey, RecipeHandlerRef> autogeneratedRecipes = new HashMap<>();
final Set<FastKey> whitelistItems = new HashSet<>(ItemList.items.size());
final FavoriteStorage storage = new FavoriteStorage();
for (ItemStack stack : ItemList.items) {
whitelistItems.add(new FastKey(stack));
}
if (interrupted()) return;
for (ICraftingHandler handler : handlers) {
if (!GuiRecipeTab.getHandlerInfo(handler).getShowFavoritesButton()) continue;
for (int i = 0; i < handler.numRecipes(); i++) {
RecipeHandlerRef ref = null;
for (PositionedStack result : getOutputs(handler, i)) {
ItemStack stack = StackInfo.getItemStackWithMinimumDamage(result.items);
final FastKey key = new FastKey(stack);
if (!whitelistItems.contains(key)) continue;
if (autogeneratedRecipes.get(key) == null) {
if (ref == null) {
ref = RecipeHandlerRef.of(handler, i);
}
autogeneratedRecipes.put(key, ref);
} else {
autogeneratedRecipes.remove(key);
whitelistItems.remove(key);
}
}
}
if (interrupted()) return;
}
final Map<RecipeHandlerRef, RecipeId> recipesCache = new HashMap<>();
for (Map.Entry<FastKey, RecipeHandlerRef> entry : autogeneratedRecipes.entrySet()) {
final RecipeId recipeId = recipesCache
.computeIfAbsent(entry.getValue(), r -> RecipeId.of(r.handler, r.recipeIndex));
storage.add(entry.getKey().stack, recipeId);
}
FavoriteRecipes.autoFavorites = storage;
updateSubsetVisibility();
}
protected List<PositionedStack> getOutputs(IRecipeHandler handler, int recipeIndex) {
final PositionedStack pStackResult = handler.getResultStack(recipeIndex);
return pStackResult != null ? Collections.singletonList(pStackResult) : handler.getOtherStacks(recipeIndex);
}
};
public static final String FAVORITE_ITEM = "FavoriteItem";
private static final FavoriteStorage manualFavorites = new FavoriteStorage();
private static FavoriteStorage autoFavorites = new FavoriteStorage();
private static File favoriteFile;
private static int revision = 0;
static {
API.addSubset("Favorites", FavoriteRecipes::contains);
API.addSubset("Favorites.Manual", manualFavorites::contains);
API.addSubset("Favorites.Generated", stack -> FavoriteRecipes.autoFavorites.contains(stack));
}
private FavoriteRecipes() {}
@@ -66,7 +171,6 @@ public class FavoriteRecipes {
final File defaultFavorites = configFavorites.exists() ? configFavorites : globalFavorites;
if (defaultFavorites.exists()) {
try {
if (favoriteFile.createNewFile()) {
InputStream src = new FileInputStream(defaultFavorites);
@@ -101,7 +205,9 @@ public class FavoriteRecipes {
}
final JsonParser parser = new JsonParser();
items.clear();
boolean hasErrors = false;
manualFavorites.clear();
for (String itemStr : itemStrings) {
@@ -117,105 +223,158 @@ public class FavoriteRecipes {
final RecipeId recipeId = RecipeId.of(jsonObject.getAsJsonObject("recipeId"));
final ItemStack stack = StackInfo.loadFromNBT(itemStackNBT);
if (stack != null) {
final String fluidKey = getFluidKey(stack);
if (fluidKey != null) {
fluids.put(fluidKey, recipeId);
}
if (recipeId == null || stack == null) {
continue;
}
items.put(itemStackNBT, recipeId);
if (NEIServerUtils.isItemTool(stack)) {
tools.add(itemStackNBT);
if (FAVORITE_ITEM.equals(recipeId.getHandlerName()) || RecipeHandlerRef.of(recipeId) != null) {
manualFavorites.add(stack, recipeId);
} else {
hasErrors = true;
}
}
} catch (IllegalArgumentException | JsonSyntaxException | IllegalStateException e) {
NEIClientConfig.logger.error("Failed to load favorite from json string:\n{}", itemStr);
} catch (Exception e) {
NEIClientConfig.logger.error("Failed to load favorite from json string: {}", itemStr);
hasErrors = true;
}
}
SubsetWidget.updateHiddenItems();
if (hasErrors) {
try {
Files.copy(
favoriteFile,
new File(favoriteFile.getAbsolutePath() + ".backup-" + System.currentTimeMillis()));
NEIClientConfig.logger.info("Backed up invalid favorite file to {}", favoriteFile);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to backup invalid favorite file to {}", favoriteFile, e);
}
save();
}
loadAutoGeneratedRecipes();
}
public static RecipeId getGeneratedFavorite(ItemStack stack) {
if (NEIClientConfig.favoritesEnabled() && stack != null) {
return autoFavorites.getRecipeId(stack);
}
return null;
}
public static ItemStack getGeneratedFavorite(RecipeId recipeId) {
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
return autoFavorites.getItemStack(recipeId);
}
return null;
}
public static RecipeId getManualFavorite(ItemStack stack) {
if (NEIClientConfig.favoritesEnabled() && stack != null) {
return manualFavorites.getRecipeId(stack);
}
return null;
}
public static ItemStack getManualFavorite(RecipeId recipeId) {
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
return manualFavorites.getItemStack(recipeId);
}
return null;
}
public static RecipeId getFavorite(ItemStack stack) {
if (NEIClientConfig.favoritesEnabled() && stack != null) {
RecipeId recipeId = items.get(StackInfo.itemStackToNBT(stack, false));
if (recipeId == null) {
recipeId = fluids.get(getFluidKey(stack));
}
if (recipeId != null) {
return recipeId;
}
if (NEIServerUtils.isItemTool(stack)
&& (stack.stackTagCompound == null || !stack.stackTagCompound.hasKey("GT.ToolStats"))) {
for (NBTTagCompound nbt : tools) {
if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(stack, StackInfo.loadFromNBT(nbt))) {
return items.get(nbt);
}
}
}
final RecipeId recipeId = manualFavorites.getRecipeId(stack);
return recipeId != null ? recipeId : autoFavorites.getRecipeId(stack);
}
return null;
}
public static ItemStack getFavorite(RecipeId recipeId) {
if (NEIClientConfig.favoritesEnabled()) {
final Optional<Map.Entry<NBTTagCompound, RecipeId>> result = items.entrySet().stream()
.filter(entry -> entry.getValue().equals(recipeId)).findAny();
if (result.isPresent()) {
return StackInfo.loadFromNBT(result.get().getKey());
}
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
final ItemStack stack = manualFavorites.getItemStack(recipeId);
return stack != null ? stack : autoFavorites.getItemStack(recipeId);
}
return null;
}
public static int size() {
return manualFavorites.size() + autoFavorites.size();
}
public static void reload() {
save();
load();
}
public static boolean hasManual() {
return manualFavorites.size() > 0;
}
public static boolean containsManual(ItemStack stack) {
return getManualFavorite(stack) != null;
}
public static boolean contains(ItemStack stack) {
return getFavorite(stack) != null;
}
public static void setFavorite(ItemStack stack, RecipeId recipeId) {
final NBTTagCompound itemStackNBT = StackInfo.itemStackToNBT(stack, false);
final String fluidKey = getFluidKey(stack);
public static void toggleFavoriteItem(ItemStack stack) {
if (stack == null) return;
items.entrySet().removeIf(entry -> entry.getKey().equals(itemStackNBT) || entry.getValue().equals(recipeId));
fluids.entrySet().removeIf(entry -> entry.getKey().equals(fluidKey) || entry.getValue().equals(recipeId));
if (recipeId != null) {
items.put(itemStackNBT, recipeId);
if (NEIServerUtils.isItemTool(stack)) {
tools.add(itemStackNBT);
}
if (fluidKey != null) {
fluids.put(fluidKey, recipeId);
}
if (containsManual(stack)) {
setFavorite(stack, null);
} else {
setFavorite(
stack,
RecipeId.of(
stack,
FavoriteRecipes.FAVORITE_ITEM,
Arrays.asList(new ItemStack(Blocks.fire, 1), stack)));
}
SubsetWidget.updateHiddenItems();
ItemList.updateFilter.restart();
}
private static String getFluidKey(ItemStack stack) {
final FluidStack fluid = StackInfo.getFluid(stack);
public static void setFavorite(ItemStack stack, RecipeId recipeId) {
if (stack == null) return;
if (fluid != null) {
return FluidRegistry.getFluidName(fluid);
manualFavorites.removeItemStack(stack);
if (recipeId != null) {
manualFavorites.removeRecipeId(recipeId);
manualFavorites.add(stack, recipeId);
}
return null;
updateSubsetVisibility();
}
public static void loadAutoGeneratedRecipes() {
if (ItemList.loadFinished
&& NEIClientConfig.getBooleanSetting("inventory.favorites.generateSingleRecipeFavorites")) {
singleRecipeFavoritesTask.restart();
} else {
singleRecipeFavoritesTask.stop();
autoFavorites.clear();
updateSubsetVisibility();
}
}
protected static void updateSubsetVisibility() {
SubsetWidget.updateHiddenItems();
FavoriteRecipes.revision++;
}
public static int getRevision() {
return revision;
}
public static void save() {
@@ -226,7 +385,7 @@ public class FavoriteRecipes {
final List<String> strings = new ArrayList<>();
for (Map.Entry<NBTTagCompound, RecipeId> entry : items.entrySet()) {
for (Map.Entry<NBTTagCompound, RecipeId> entry : manualFavorites.getAllFavorites()) {
try {
final JsonObject line = new JsonObject();
@@ -38,6 +38,10 @@ public class FormattedTextField extends GuiTextField {
this.formatter = formatter;
}
public TextFormatter getFormatter() {
return this.formatter;
}
public void setPlaceholder(String placeholder) {
this.placeholder = placeholder;
}
@@ -57,6 +61,11 @@ public class FormattedTextField extends GuiTextField {
super.setFocused(focus);
}
@Override
public void setText(String rawText) {
super.setText(EnumChatFormatting.getTextWithoutFormattingCodes(rawText));
}
@Override
public void setSelectionPos(int p_146199_1_) {
String text = this.getText();
@@ -269,7 +278,7 @@ public class FormattedTextField extends GuiTextField {
}
Tessellator tessellator = Tessellator.instance;
GL11.glColor4f(0.0F, 0.0F, 255.0F, 255.0F);
GL11.glColor4f(0.0F, 0.0F, 1.0F, 1.0F);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_COLOR_LOGIC_OP);
GL11.glLogicOp(GL11.GL_OR_REVERSE);
@@ -287,15 +296,26 @@ public class FormattedTextField extends GuiTextField {
int shift = 0;
for (int i = 0; i < position; i++) {
while (this.formattedText.length() > i + shift && this.formattedText.charAt(i + shift) == '\u00a7') {
while (this.formattedText.length() > i + shift + 1 && this.formattedText.charAt(i + shift) == '\u00a7'
&& isFormattingCode(this.formattedText.charAt(i + shift + 1))) {
shift += 2;
}
}
return position + shift;
return Math.min(position + shift, this.formattedText.length());
}
private static boolean isFormattingCode(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
|| (c >= 'k' && c <= 'o')
|| c == 'r'
|| (c >= 'A' && c <= 'F')
|| (c >= 'K' && c <= 'O')
|| c == 'R';
}
private String getPreviousColor(int position) {
position = Math.min(position, this.formattedText.length() - 1);
while (position >= 0) {
if (this.formattedText.charAt(position) == '\u00a7') {
return this.formattedText.substring(position, position + 2);
@@ -24,8 +24,8 @@ public class GuiEnchantmentModifier extends GuiContainer {
@Override
protected void drawGuiContainerForegroundLayer(int par1, int par2) {
fontRendererObj.drawString(translate("enchant"), 12, 6, 0x404040);
fontRendererObj.drawString(translate("enchant.level"), 19, 20, 0x404040);
fontRendererObj.drawString(translate("enchant"), 12, 6, ColorUtils.textGray.getColor());
fontRendererObj.drawString(translate("enchant.level"), 19, 20, ColorUtils.textGray.getColor());
}
protected void drawGuiContainerBackgroundLayer(float f, int i, int j) {
@@ -39,7 +39,11 @@ public class GuiEnchantmentModifier extends GuiContainer {
container.drawScrollBar(this);
String levelstring = "" + container.level;
fontRendererObj.drawString(levelstring, 33 - fontRendererObj.getStringWidth(levelstring) / 2, 34, 0xFF606060);
fontRendererObj.drawString(
levelstring,
33 - fontRendererObj.getStringWidth(levelstring) / 2,
34,
ColorUtils.textLightGray.getColor());
GL11.glTranslatef(-guiLeft, -guiTop, 0);
}
@@ -78,8 +82,8 @@ public class GuiEnchantmentModifier extends GuiContainer {
private void changeLevel(int i) {
container.level += i;
((GuiButton) buttonList.get(0)).enabled = container.level != 1;
((GuiButton) buttonList.get(1)).enabled = container.level != 10;
buttonList.get(0).enabled = container.level != 1;
buttonList.get(1).enabled = container.level != 10;
}
protected void mouseClicked(int i, int j, int k) {
+21 -26
View File
@@ -7,56 +7,51 @@ import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
public class GuiNEIButton extends GuiButton {
protected static final ResourceLocation guiTex = new ResourceLocation("textures/gui/widgets.png");
private static final DrawableResource[] BACKGROUNDS = new DrawableResource[3];
static {
for (int state = 0; state < BACKGROUNDS.length; state++) {
BACKGROUNDS[state] = new DrawableBuilder(guiTex, 0, 46 + state * 20, 200, 20).setSlices(2, 2, 2, 2).build();
}
}
public GuiNEIButton(int i, int j, int k, int l, int i1, String s) {
super(i, j, k, l, i1, s);
}
protected DrawableResource getBackground(int mouseX, int mouseY, boolean mouseOver) {
return BACKGROUNDS[getHoverState(mouseOver)];
}
public void drawButton(Minecraft minecraft, int x, int y) {
if (!visible) return;
minecraft.renderEngine.bindTexture(guiTex);
GL11.glColor4f(1, 1, 1, 1);
boolean mouseOver = x >= xPosition && y >= yPosition && x < xPosition + width && y < yPosition + height;
int k = getHoverState(mouseOver);
drawTexturedModalRect(xPosition, yPosition, 0, 46 + k * 20, width / 2, height / 2); // top left
drawTexturedModalRect(xPosition + width / 2, yPosition, 200 - width / 2, 46 + k * 20, width / 2, height / 2); // top
// right
drawTexturedModalRect(
xPosition,
yPosition + height / 2,
0,
46 + k * 20 + 20 - height / 2,
width / 2,
height / 2); // bottom left
drawTexturedModalRect(
xPosition + width / 2,
yPosition + height / 2,
200 - width / 2,
46 + k * 20 + 20 - height / 2,
width / 2,
height / 2); // bottom right
mouseDragged(minecraft, x, y);
GL11.glColor4f(1, 1, 1, 1);
getBackground(x, y, mouseOver).draw(xPosition, yPosition, width, height);
mouseDragged(minecraft, x, y);
drawContent(minecraft, x, y, mouseOver);
}
protected int getTextColour(boolean mouseOver) {
int color = 0xe0e0e0;
if (!enabled) {
color = 0xffa0a0a0;
return ColorUtils.buttonLabelInactive.getColor();
} else if (mouseOver) {
color = 0xffffa0;
return ColorUtils.buttonLabelHover.getColor();
}
return color;
return ColorUtils.buttonLabelNormal.getColor();
}
protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) {
protected void drawContent(Minecraft minecraft, int x, int y, boolean mouseOver) {
FontRenderer fontrenderer = minecraft.fontRenderer;
int color = getTextColour(mouseOver);
@@ -289,10 +289,10 @@ public class GuiPotionCreator extends GuiContainerWidget {
CCRenderState.changeTexture("nei:textures/gui/potion.png");
drawTexturedModalRect(0, 0, 0, 0, xSize, ySize);
FontUtils.drawCenteredString(translate("potion.favourite"), xSize / 2, 4, 0x404040);
fontRendererObj.drawString(translate("potion.duration"), 12, 40, 0x404040);
fontRendererObj.drawString(translate("potion.level"), 19, 73, 0x404040);
FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, 0xFF606060);
FontUtils.drawCenteredString(translate("potion.favourite"), xSize / 2, 4, ColorUtils.textGray.getColor());
fontRendererObj.drawString(translate("potion.duration"), 12, 40, ColorUtils.textGray.getColor());
fontRendererObj.drawString(translate("potion.level"), 19, 73, ColorUtils.textGray.getColor());
FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, ColorUtils.textGray.getColor());
}
@Override
@@ -28,7 +28,7 @@ public class HUDRenderer implements IKeyStateTracker {
@Override
public void tickKeyStates() {
if (KeyManager.keyStates.get("world.highlight_tips").down) {
if (KeyManager.isPressed("world.highlight_tips")) {
ConfigTag tag = NEIClientConfig.getSetting("world.highlight_tips");
tag.setBooleanValue(!tag.getBooleanValue());
}
@@ -42,13 +42,11 @@ public class HUDRenderer implements IKeyStateTracker {
&& mc.objectMouseOver != null
&& mc.objectMouseOver.typeOfHit == MovingObjectType.BLOCK) {
World world = mc.theWorld;
ItemStack[] items = ItemInfo.getIdentifierItems(world, mc.thePlayer, mc.objectMouseOver)
.toArray(new ItemStack[0]);
final List<ItemStack> items = ItemInfo.getIdentifierItems(world, mc.thePlayer, mc.objectMouseOver);
if (items.length == 0) return;
ItemStack stack = StackInfo.getItemStackWithMinimumDamage(items);
if (items.isEmpty()) return;
final ItemStack stack = StackInfo.getItemStackWithMinimumDamage(items.toArray(new ItemStack[0]));
renderOverlay(stack, ItemInfo.getText(stack, world, mc.thePlayer, mc.objectMouseOver), getPositioning());
}
}
@@ -27,8 +27,8 @@ public class HeldItemHandler implements IContainerInputHandler, IContainerToolti
protected boolean contains(int mousex, int mousey) {
return NEIClientUtils.getHeldItem() != null && !ItemPanels.bookmarkPanel.inEditingState()
&& !AutoCraftingManager.processing()
&& (ItemPanels.bookmarkPanel.contains(mousex, mousey) || ItemPanels.itemPanel.contains(mousex, mousey)
|| ItemPanels.itemPanel.historyPanel.contains(mousex, mousey));
&& (ItemPanels.bookmarkPanel.contains(mousex, mousey)
|| ItemPanels.itemPanel.containsWithSubpanels(mousex, mousey));
}
@Override
@@ -36,7 +36,8 @@ public class HeldItemHandler implements IContainerInputHandler, IContainerToolti
if (contains(mousex, mousey)) {
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")) {
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")
|| NEIClientUtils.mc().playerController.isInCreativeMode()) {
if (button == 1) {
NEIClientUtils.decreaseSlotStack(-999);
} else {
@@ -101,7 +102,8 @@ public class HeldItemHandler implements IContainerInputHandler, IContainerToolti
if (contains(mousex, mousey)) {
currenttip.clear();
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")) {
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")
|| NEIClientUtils.mc().playerController.isInCreativeMode()) {
currenttip.add(EnumChatFormatting.RED + translate("itempanel.deleteItem"));
}
}
@@ -0,0 +1,161 @@
package codechicken.nei;
import net.minecraft.client.gui.GuiRepair;
import net.minecraft.client.gui.inventory.GuiBrewingStand;
import net.minecraft.client.gui.inventory.GuiCrafting;
import net.minecraft.client.gui.inventory.GuiFurnace;
import net.minecraft.client.gui.inventory.GuiInventory;
import net.minecraft.nbt.NBTTagCompound;
import codechicken.nei.api.API;
import codechicken.nei.recipe.BrewingOverlayHandler;
import codechicken.nei.recipe.BrewingRecipeHandler;
import codechicken.nei.recipe.DefaultOverlayHandler;
import codechicken.nei.recipe.FireworkRecipeHandler;
import codechicken.nei.recipe.FuelRecipeHandler;
import codechicken.nei.recipe.FurnaceRecipeHandler;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.recipe.ProfilerRecipeHandler;
import codechicken.nei.recipe.RepairOverlayHandler;
import codechicken.nei.recipe.RepairRecipeHandler;
import codechicken.nei.recipe.ShapedRecipeHandler;
import codechicken.nei.recipe.ShapelessRecipeHandler;
import cpw.mods.fml.common.event.FMLInterModComms;
public class IMCForNEI {
public static void IMCSender() {
API.registerRecipeHandler(new ShapedRecipeHandler());
API.registerUsageHandler(new ShapedRecipeHandler());
sendHandler("codechicken.nei.recipe.ShapedRecipeHandler", "minecraft:crafting_table");
API.registerRecipeHandler(new ShapelessRecipeHandler());
API.registerUsageHandler(new ShapelessRecipeHandler());
sendHandler("codechicken.nei.recipe.ShapelessRecipeHandler", "minecraft:crafting_table");
API.registerRecipeHandler(new FireworkRecipeHandler());
API.registerUsageHandler(new FireworkRecipeHandler());
sendHandler("codechicken.nei.recipe.FireworkRecipeHandler", "minecraft:fireworks");
API.registerRecipeHandler(new FurnaceRecipeHandler());
API.registerUsageHandler(new FurnaceRecipeHandler());
sendHandler("codechicken.nei.recipe.FurnaceRecipeHandler", "minecraft:furnace");
API.registerRecipeHandler(new BrewingRecipeHandler());
API.registerUsageHandler(new BrewingRecipeHandler());
sendHandler("codechicken.nei.recipe.BrewingRecipeHandler", "minecraft:brewing_stand");
API.registerRecipeHandler(new FuelRecipeHandler());
API.registerUsageHandler(new FuelRecipeHandler());
sendFuelRecipeHandler();
API.registerRecipeHandler(new InformationHandler());
API.registerUsageHandler(new InformationHandler());
sendInformationHandler();
API.registerRecipeHandler(new RepairRecipeHandler());
API.registerUsageHandler(new RepairRecipeHandler());
sendRepairHandler();
sendCatalyst("repair", "minecraft:anvil");
API.registerGuiOverlay(GuiCrafting.class, "crafting");
API.registerGuiOverlay(GuiInventory.class, "crafting2x2", 63, 20);
API.registerGuiOverlay(GuiFurnace.class, "smelting");
API.registerGuiOverlay(GuiFurnace.class, "fuel");
API.registerGuiOverlay(GuiBrewingStand.class, "brewing");
API.registerGuiOverlay(GuiRepair.class, "repair", 2, 37);
API.registerGuiOverlayHandler(GuiCrafting.class, new DefaultOverlayHandler(), "crafting");
API.registerGuiOverlayHandler(GuiInventory.class, new DefaultOverlayHandler(63, 20), "crafting2x2");
API.registerGuiOverlayHandler(GuiBrewingStand.class, new BrewingOverlayHandler(), "brewing");
API.registerGuiOverlayHandler(GuiRepair.class, new RepairOverlayHandler(), "repair");
API.registerRecipeHandler(new ProfilerRecipeHandler(true));
API.registerUsageHandler(new ProfilerRecipeHandler(false));
sendProfilerRecipeHandler();
}
private static void sendHandler(String aName, String aBlock) {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handler", aName);
aNBT.setString("modName", "Minecraft");
aNBT.setString("modId", "minecraft");
aNBT.setString("itemName", aBlock);
aNBT.setInteger("handlerWidth", 166);
aNBT.setInteger("handlerHeight", 65);
aNBT.setBoolean("multipleWidgetsAllowed", true);
aNBT.setInteger("yShift", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendRepairHandler() {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handler", "codechicken.nei.recipe.RepairRecipeHandler");
aNBT.setString("modName", "Minecraft");
aNBT.setString("modId", "minecraft");
aNBT.setString("itemName", "minecraft:anvil");
aNBT.setInteger("handlerHeight", 39);
aNBT.setInteger("handlerWidth", 166);
aNBT.setBoolean("multipleWidgetsAllowed", true);
aNBT.setBoolean("showFavoritesButton", false);
aNBT.setInteger("yShift", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendFuelRecipeHandler() {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handler", "codechicken.nei.recipe.FuelRecipeHandler");
aNBT.setString("modName", "Minecraft");
aNBT.setString("modId", "minecraft");
aNBT.setString("imageResource", "nei:textures/nei_tabbed_sprites.png");
aNBT.setInteger("imageX", 80);
aNBT.setInteger("imageY", 0);
aNBT.setInteger("imageWidth", 14);
aNBT.setInteger("imageHeight", 14);
aNBT.setInteger("handlerHeight", 65);
aNBT.setInteger("handlerWidth", 166);
aNBT.setBoolean("multipleWidgetsAllowed", true);
aNBT.setInteger("yShift", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendInformationHandler() {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handler", "codechicken.nei.recipe.InformationHandler");
aNBT.setString("modName", "Not Enough Items");
aNBT.setString("modId", "nei");
aNBT.setString("imageResource", "nei:textures/nei_tabbed_sprites.png");
aNBT.setInteger("imageX", 96);
aNBT.setInteger("imageY", 0);
aNBT.setInteger("imageWidth", 16);
aNBT.setInteger("imageHeight", 16);
aNBT.setInteger("handlerHeight", 65);
aNBT.setInteger("handlerWidth", 166);
aNBT.setBoolean("multipleWidgetsAllowed", true);
aNBT.setInteger("yShift", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendProfilerRecipeHandler() {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handler", "codechicken.nei.recipe.ProfilerRecipeHandler");
aNBT.setString("modName", "Not Enough Items");
aNBT.setString("modId", "nei");
aNBT.setString("itemName", "minecraft:clock");
aNBT.setInteger("handlerHeight", 16);
aNBT.setInteger("handlerWidth", 166);
aNBT.setBoolean("multipleWidgetsAllowed", true);
aNBT.setInteger("yShift", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
}
private static void sendCatalyst(String aName, String aStack) {
NBTTagCompound aNBT = new NBTTagCompound();
aNBT.setString("handlerID", aName);
aNBT.setString("itemName", aStack);
aNBT.setInteger("priority", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerCatalystInfo", aNBT);
}
}
@@ -30,14 +30,7 @@ public class InfiniteStackSizeHandler implements IInfiniteItemHandler {
@Override
public void replenishInfiniteStack(InventoryPlayer inv, int slotNo) {
ItemStack stack = inv.getStackInSlot(slotNo);
stack.stackSize = 111;
for (int i = 0; i < inv.getSizeInventory(); i++) {
if (i == slotNo) continue;
if (NEIServerUtils.areStacksSameType(stack, inv.getStackInSlot(i))) inv.setInventorySlotContents(i, null);
}
inv.getStackInSlot(slotNo).stackSize = 111;
}
@Override
@@ -2,14 +2,18 @@ package codechicken.nei;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidContainerRegistry;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
@@ -20,7 +24,6 @@ import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.recipe.StackInfo;
@@ -38,7 +41,7 @@ public class ItemCraftablesPanel
public CraftablesGridSlot(int slotIndex, int itemIndex, ItemStack itemStack, RecipeId recipeId) {
super(slotIndex, itemIndex, itemStack);
this.realAmount = StackInfo.getAmount(itemStack);
this.isFluidDisplay = StackInfo.itemStackToNBT(itemStack).hasKey("gtFluidName");
this.isFluidDisplay = StackInfo.isFluidDisplayItem(itemStack);
this.recipeId = recipeId;
}
@@ -81,6 +84,42 @@ public class ItemCraftablesPanel
}
private static class CraftablesSnapshot {
final GuiContainer guiContainer;
final ItemStackAmount inventory;
final boolean showOnlyFavorites;
final int maxCandidates;
CraftablesSnapshot(GuiContainer guiContainer, ItemStackAmount inventory, boolean showOnlyFavorites,
int maxCandidates) {
this.guiContainer = guiContainer;
this.inventory = inventory;
this.showOnlyFavorites = showOnlyFavorites;
this.maxCandidates = maxCandidates;
}
}
private static class RecipeCandidate {
final IRecipeHandler handler;
final int recipeIndex;
final List<PositionedStack> ingredientStacks;
final ItemStack sortKeyItem;
RecipeCandidate(IRecipeHandler handler, int recipeIndex, List<PositionedStack> ingredientStacks,
ItemStack sortKeyItem) {
this.handler = handler;
this.recipeIndex = recipeIndex;
this.ingredientStacks = ingredientStacks;
this.sortKeyItem = sortKeyItem;
}
}
private static final Comparator<ItemStack> ITEM_SORT_COMPARATOR = Comparator
.comparing(FavoriteRecipes::containsManual).thenComparing(FavoriteRecipes::contains).reversed()
.thenComparing(ItemSorter.instance);
public static List<String> guiBlacklist = new ArrayList<>();
private final int MIN_DELAY = 600;
@@ -90,6 +129,17 @@ public class ItemCraftablesPanel
private GuiContainer lastGuiContainer = null;
private ItemStackAmount lastPlayerInventory = null;
private long lastPlayerInventorySync = 0;
private int lastFavoritesCount = 0;
private volatile CraftablesSnapshot pendingSnapshot;
private final AtomicReference<Map<ItemStack, RecipeId>> pendingResult = new AtomicReference<>();
private final RestartableTask task = new RestartableTask("NEI Craftables Panel") {
@Override
public void execute() {
pendingResult.set(generateCraftables(ItemCraftablesPanel.this.pendingSnapshot));
}
};
public ItemCraftablesPanel() {
this.grid = new ItemsGrid<>() {
@@ -106,28 +156,38 @@ public class ItemCraftablesPanel
public List<CraftablesGridSlot> getMask() {
if (this.gridMask == null) {
int maxSlotIndex = this.rows * this.columns;
final AtomicInteger slotIndex = new AtomicInteger(0);
final int maxSlotIndex = this.rows * this.columns;
final List<CraftablesGridSlot> gridMask = new ArrayList<>();
this.realItems.clear();
if (!ItemCraftablesPanel.this.availableRecipes.isEmpty()) {
ItemCraftablesPanel.this.availableRecipes.entrySet().stream()
.sorted(Map.Entry.comparingByKey(ItemSorter.instance)).forEach(entry -> {
this.realItems.add(entry.getKey());
gridMask.add(
new CraftablesGridSlot(
slotIndex.get(),
slotIndex.get(),
entry.getKey(),
entry.getValue()));
slotIndex.incrementAndGet();
});
final List<Map.Entry<ItemStack, RecipeId>> entries = ItemCraftablesPanel.this.availableRecipes
.entrySet().stream().sorted(Map.Entry.comparingByKey(ITEM_SORT_COMPARATOR))
.limit(maxSlotIndex).collect(Collectors.toList());
int slotIndex = 0;
int itemIndex = 0;
this.gridMask = gridMask.subList(0, Math.min(gridMask.size(), maxSlotIndex));
for (Map.Entry<ItemStack, RecipeId> entry : entries) {
if (!isInvalidSlot(slotIndex)) {
this.realItems.add(entry.getKey());
gridMask.add(
new CraftablesGridSlot(
slotIndex,
itemIndex++,
entry.getKey(),
entry.getValue()));
}
if (slotIndex++ >= maxSlotIndex) {
break;
}
}
this.gridMask = gridMask;
} else {
this.gridMask = Collections.emptyList();
}
ItemCraftablesPanel.this.updateLinePadding();
}
return this.gridMask;
@@ -178,10 +238,20 @@ public class ItemCraftablesPanel
public void update() {
this.splittingLineColor = NEIClientConfig.getSetting("inventory.craftables.color").getHexValue();
applyPendingResult();
updateCraftables();
super.update();
}
private void applyPendingResult() {
final Map<ItemStack, RecipeId> result = this.pendingResult.getAndSet(null);
if (result != null && NEIClientConfig.showCraftablesPanelWidget()) {
this.availableRecipes = result;
this.grid.onGridChanged();
}
}
private void updateCraftables() {
if (!NEIClientConfig.showCraftablesPanelWidget()) {
@@ -192,13 +262,24 @@ public class ItemCraftablesPanel
this.lastPlayerInventorySync = System.currentTimeMillis();
if (firstGui != null && !ItemCraftablesPanel.guiBlacklist.contains(firstGui.getClass().getName())) {
final boolean showOnlyFavorites = NEIClientConfig
.getBooleanSetting("inventory.craftables.favoritesOnly");
final ItemStackAmount inv = AutoCraftingManager.getInventoryItems(firstGui);
if (this.lastGuiContainer != (this.lastGuiContainer = firstGui)
if (this.lastGuiContainer != firstGui
|| showOnlyFavorites && this.lastFavoritesCount != FavoriteRecipes.size()
|| !inv.equals(this.lastPlayerInventory)) {
this.lastPlayerInventory = inv;
this.availableRecipes = generateCraftables(firstGui);
this.grid.onGridChanged();
this.lastGuiContainer = firstGui;
this.lastFavoritesCount = FavoriteRecipes.size();
final int maxRows = NEIClientConfig.getIntSetting("inventory.craftables.useRows") + 2;
final int maxCandidates = Math.max(this.grid.columns, 1) * maxRows;
this.pendingSnapshot = new CraftablesSnapshot(
firstGui,
inv,
showOnlyFavorites,
maxCandidates);
this.task.restart();
}
} else {
@@ -219,20 +300,31 @@ public class ItemCraftablesPanel
private GuiContainer getGuiContainer() {
final GuiContainer firstGui = NEIClientUtils.getGuiContainer();
return (firstGui instanceof GuiRecipe gui) ? gui.firstGui : firstGui;
return (firstGui instanceof GuiRecipe<?>gui) ? gui.firstGui : firstGui;
}
private Map<ItemStack, RecipeId> generateCraftables(GuiContainer firstGui) {
final List<IRecipeHandler> availableHandlers = getAvailableHandlers(firstGui);
private Map<ItemStack, RecipeId> generateCraftables(CraftablesSnapshot snapshot) {
final List<IRecipeHandler> availableHandlers = getAvailableHandlers(snapshot.guiContainer);
final Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
if (!availableHandlers.isEmpty()) {
final List<ItemStack> invStacks = ItemCraftablesPanel.this.lastPlayerInventory.values();
final Map<Item, List<ItemStack>> invByItem = indexInventoryByItem(snapshot.inventory.values());
for (IRecipeHandler handler : availableHandlers) {
availableRecipes.putAll(filterHandlerRecipes(handler, firstGui, invStacks));
if (snapshot.showOnlyFavorites) {
for (IRecipeHandler handler : availableHandlers) {
availableRecipes.putAll(filterHandlerRecipes(handler, invByItem, true));
}
} else {
final List<RecipeCandidate> candidates = Collections.synchronizedList(new ArrayList<>());
for (IRecipeHandler handler : availableHandlers) {
collectHandlerCandidates(handler, invByItem, candidates);
}
candidates.sort(Comparator.comparing(c -> c.sortKeyItem, ITEM_SORT_COMPARATOR));
candidates.subList(0, Math.min(candidates.size(), snapshot.maxCandidates)).parallelStream()
.forEach(candidate -> buildCandidate(candidate, availableRecipes));
}
}
return availableRecipes;
@@ -248,17 +340,126 @@ public class ItemCraftablesPanel
return availableHandlers;
}
private Map<ItemStack, RecipeId> filterHandlerRecipes(IRecipeHandler handler, GuiContainer firstGui,
List<ItemStack> invStacks) {
private static Map<Item, List<ItemStack>> indexInventoryByItem(List<ItemStack> invStacks) {
final Map<Item, List<ItemStack>> index = new HashMap<>();
for (ItemStack stack : invStacks) {
index.computeIfAbsent(stack.getItem(), i -> new ArrayList<>()).add(stack);
}
return index;
}
private void collectHandlerCandidates(IRecipeHandler handler, Map<Item, List<ItemStack>> invByItem,
List<RecipeCandidate> candidates) {
IntStream.range(0, handler.numRecipes()).parallel().forEach(recipeIndex -> {
final List<PositionedStack> ingredientStacks = handler.getIngredientStacks(recipeIndex);
if (existsIngredients(ingredientStacks, invByItem)) {
final ItemStack sortKeyItem = getCheapResultItem(handler, recipeIndex);
if (sortKeyItem != null) {
candidates.add(new RecipeCandidate(handler, recipeIndex, ingredientStacks, sortKeyItem));
}
}
});
}
private void buildCandidate(RecipeCandidate candidate, Map<ItemStack, RecipeId> availableRecipes) {
final ItemStack result = getCheapResult(candidate.handler, candidate.recipeIndex);
if (result != null) {
final RecipeId recipeId = RecipeId.of(candidate.handler, candidate.recipeIndex, candidate.ingredientStacks);
synchronized (availableRecipes) {
availableRecipes.put(result, recipeId);
}
}
}
private static ItemStack getCheapResultItem(IRecipeHandler handler, int recipeIndex) {
final PositionedStack resultStack = handler.getResultStack(recipeIndex);
if (resultStack != null) {
return resultStack.item;
}
final List<PositionedStack> otherStacks = handler.getOtherStacks(recipeIndex);
for (PositionedStack otherStack : otherStacks) {
if (!FluidContainerRegistry.isContainer(otherStack.items[0])
|| StackInfo.getFluid(otherStack.items[0]) != null) {
return otherStack.item;
}
}
return otherStacks.isEmpty() ? null : otherStacks.get(0).item;
}
private static ItemStack getCheapResult(IRecipeHandler handler, int recipeIndex) {
final PositionedStack resultStack = handler.getResultStack(recipeIndex);
final List<PositionedStack> results = resultStack != null ? Collections.singletonList(resultStack)
: handler.getOtherStacks(recipeIndex);
if (results.isEmpty()) {
return null;
}
ItemStack stack = null;
if (results.size() > 1) {
for (PositionedStack result : results) {
if (!FluidContainerRegistry.isContainer(result.item) || StackInfo.getFluid(result.item) != null) {
stack = result.item;
break;
}
}
}
if (stack == null) {
stack = results.get(0).item;
}
int stackSize = 0;
for (PositionedStack result : results) {
if (containsSameType(result, stack)) {
stackSize += StackInfo.getAmount(result.item);
}
}
return StackInfo.withAmount(stack, stackSize);
}
private static boolean containsSameType(PositionedStack pStack, ItemStack stack) {
final ItemStack normalized = StackInfo.withAmount(stack, 0);
for (ItemStack permutation : pStack.items) {
if (NEIServerUtils.areStacksSameTypeWithNBT(StackInfo.withAmount(permutation, 0), normalized)) {
return true;
}
}
return false;
}
private Map<ItemStack, RecipeId> filterHandlerRecipes(IRecipeHandler handler, Map<Item, List<ItemStack>> invByItem,
boolean showOnlyFavorites) {
final Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
IntStream.range(0, handler.numRecipes()).parallel().forEach(recipeIndex -> {
if (existsIngredients(firstGui, handler.getIngredientStacks(recipeIndex), invStacks)) {
final Recipe recipe = Recipe.of(handler, recipeIndex);
final List<PositionedStack> ingredientStacks = handler.getIngredientStacks(recipeIndex);
if (!recipe.getResults().isEmpty()) {
synchronized (availableRecipes) {
availableRecipes.put(recipe.getResult(), recipe.getRecipeId());
if (existsIngredients(ingredientStacks, invByItem)) {
final ItemStack result = getCheapResult(handler, recipeIndex);
if (result != null) {
final RecipeId recipeId = RecipeId.of(handler, recipeIndex, ingredientStacks);
if (!showOnlyFavorites || FavoriteRecipes.getFavorite(recipeId) != null) {
synchronized (availableRecipes) {
availableRecipes.put(result, recipeId);
}
}
}
}
@@ -267,14 +468,27 @@ public class ItemCraftablesPanel
return availableRecipes;
}
private boolean existsIngredients(GuiContainer firstGui, List<PositionedStack> ingredients,
List<ItemStack> invStacks) {
private boolean existsIngredients(List<PositionedStack> ingredients, Map<Item, List<ItemStack>> invByItem) {
final Map<ItemStack, Integer> usedItems = new HashMap<>();
for (PositionedStack pStack : ingredients) {
final ItemStack used = invStacks.stream().filter(
is -> usedItems.getOrDefault(is, is.stackSize) >= pStack.item.stackSize && pStack.contains(is))
.findAny().orElse(null);
ItemStack used = null;
for (ItemStack permutation : pStack.items) {
final List<ItemStack> candidates = invByItem.get(permutation.getItem());
if (candidates == null) continue;
for (ItemStack is : candidates) {
if (usedItems.getOrDefault(is, is.stackSize) >= pStack.item.stackSize
&& NEIServerUtils.areStacksSameTypeCrafting(permutation, is)) {
used = is;
break;
}
}
if (used != null) break;
}
if (used != null) {
usedItems.put(used, usedItems.getOrDefault(used, used.stackSize) - pStack.item.stackSize);
@@ -1,20 +1,50 @@
package codechicken.nei;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import org.apache.commons.io.IOUtils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import codechicken.core.CommonUtils;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemsGridSlot, MouseContext>> {
public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemHistoryPanel.HistoryGridSlot, MouseContext>> {
protected File historyFile = null;
public static class HistoryGridSlot extends ItemsGridSlot {
public HistoryGridSlot(int slotIndex, int itemIndex, ItemStack item) {
super(slotIndex, itemIndex, item);
}
@Override
public RecipeId getRecipeId() {
return FavoriteRecipes.getFavorite(this.item);
}
}
public ItemHistoryPanel() {
this.grid = new ItemsGrid<>() {
protected List<ItemsGridSlot> gridMask;
protected List<HistoryGridSlot> gridMask;
@Override
protected void onGridChanged() {
@@ -23,13 +53,19 @@ public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemsGridSlot,
}
@Override
public List<ItemsGridSlot> getMask() {
public List<HistoryGridSlot> getMask() {
if (this.gridMask == null) {
this.gridMask = new ArrayList<>();
for (int slotIndex = 0; slotIndex < Math.min(size(), this.rows * this.columns); slotIndex++) {
this.gridMask.add(new ItemsGridSlot(slotIndex, slotIndex, getItem(slotIndex)));
int itemIndex = 0;
for (int slotIndex = 0; slotIndex < this.rows * this.columns && itemIndex < size(); slotIndex++) {
if (!isInvalidSlot(slotIndex)) {
this.gridMask.add(new HistoryGridSlot(slotIndex, itemIndex, getItem(itemIndex)));
itemIndex++;
}
}
ItemHistoryPanel.this.updateLinePadding();
}
return this.gridMask;
@@ -37,7 +73,7 @@ public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemsGridSlot,
@Override
protected MouseContext getMouseContext(int mousex, int mousey) {
final ItemsGridSlot hovered = getSlotMouseOver(mousex, mousey);
final HistoryGridSlot hovered = getSlotMouseOver(mousex, mousey);
if (hovered != null) {
return new MouseContext(
@@ -96,4 +132,107 @@ public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemsGridSlot,
}
}
public void load() {
if (!NEIClientConfig.getBooleanSetting("inventory.history.save")) {
return;
}
String worldPath = "global";
if (NEIClientConfig.getBooleanSetting("inventory.history.worldSpecific")) {
worldPath = NEIClientConfig.getWorldPath();
}
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
if (!dir.exists()) {
dir.mkdirs();
}
final File historyFile = new File(dir, "history.ini");
if (historyFile.equals(this.historyFile)) {
return;
}
List<String> itemStrings = Collections.emptyList();
if (historyFile.exists()) {
try (FileInputStream reader = new FileInputStream(historyFile)) {
NEIClientConfig.logger.info("Loading history from file {}", historyFile);
itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load history from file {}", historyFile, e);
return;
}
}
final JsonParser parser = new JsonParser();
final int maxSize = Math.max(50, this.grid.rows * this.grid.columns);
this.grid.realItems.clear();
for (String itemStr : itemStrings) {
if (itemStr.isEmpty()) {
continue;
}
if (this.grid.realItems.size() >= maxSize) {
break;
}
try {
JsonObject row = parser.parse(itemStr).getAsJsonObject();
NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(row.get("item"));
ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT);
if (itemStack != null) {
this.grid.realItems.add(itemStack);
} else {
NEIClientConfig.logger.warn(
"Failed to load history ItemStack from json string, the item no longer exists:\n{}",
itemStr);
}
} catch (Exception e) {
NEIClientConfig.logger.error("Failed to load history ItemStack from json string:\n{}", itemStr);
}
}
this.grid.onItemsChanged();
this.historyFile = historyFile;
}
public void save() {
if (!NEIClientConfig.getBooleanSetting("inventory.history.save")) {
return;
}
if (this.historyFile == null) {
return;
}
final List<String> strings = new ArrayList<>();
for (ItemStack stack : this.grid.realItems) {
try {
final JsonObject row = new JsonObject();
row.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(stack)));
strings.add(NBTJson.toJson(row));
} catch (JsonSyntaxException e) {
NEIClientConfig.logger.error("Failed to stringify history ItemStack to json string");
}
}
try (FileOutputStream output = new FileOutputStream(this.historyFile)) {
IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save history list to file {}", this.historyFile, e);
}
}
}
+134 -205
View File
@@ -1,33 +1,25 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import java.util.regex.Pattern;
import net.minecraft.client.Minecraft;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.StatCollector;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import codechicken.nei.ThreadOperationTimer.TimeoutException;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.filter.AllMultiItemFilter;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.search.TooltipFilter;
import codechicken.nei.util.ItemUntranslator;
public class ItemList {
@@ -45,14 +37,136 @@ public class ItemList {
public static final List<ItemFilterProvider> itemFilterers = new LinkedList<>();
public static final List<ItemsLoadedCallback> loadCallbacks = new LinkedList<>();
private static final HashSet<Item> erroredItems = new HashSet<>();
private static final HashSet<String> stackTraces = new HashSet<>();
private static Map<ItemStack, Integer> ordering = new HashMap<>();
/**
* Unlike {@link LayoutManager#itemsLoaded}, this indicates whether item loading is actually finished or not.
*/
public static boolean loadFinished;
public static class EverythingItemFilter implements ItemFilter {
@Override
public boolean matches(ItemStack item) {
return true;
}
}
public static class NothingItemFilter implements ItemFilter {
@Override
public boolean matches(ItemStack item) {
return false;
}
}
public static class NegatedItemFilter implements ItemFilter {
public ItemFilter filter;
public NegatedItemFilter(ItemFilter filter) {
this.filter = filter;
}
@Override
public boolean matches(ItemStack item) {
return this.filter == null || !this.filter.matches(item);
}
}
public static class PatternItemFilter implements ItemFilter {
public Pattern pattern;
public PatternItemFilter(Pattern pattern) {
this.pattern = pattern;
}
@Override
public boolean matches(ItemStack item) {
String displayName = EnumChatFormatting.getTextWithoutFormattingCodes(item.getDisplayName());
if (displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find()) {
return true;
}
displayName = ItemUntranslator.getInstance().getItemStackDisplayName(item);
if (!displayName.isEmpty() && this.pattern.matcher(displayName).find()) {
return true;
}
if (item.hasDisplayName()) {
displayName = EnumChatFormatting
.getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item));
if (displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find()) {
return true;
}
}
for (String alias : ItemInfo.getAliases(item)) {
if (this.pattern.matcher(StatCollector.translateToLocal(alias)).find()) {
return true;
}
}
return false;
}
}
public static class AllMultiItemFilter implements ItemFilter {
public List<ItemFilter> filters;
public AllMultiItemFilter(List<ItemFilter> filters) {
this.filters = filters;
}
public AllMultiItemFilter(ItemFilter... filters) {
this(new LinkedList<>(Arrays.asList(filters)));
}
public AllMultiItemFilter() {
this(new LinkedList<>());
}
@Override
public boolean matches(ItemStack item) {
for (ItemFilter filter : filters) try {
if (filter != null && !filter.matches(item)) return false;
} catch (Exception e) {
NEIClientConfig.logger
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
}
return true;
}
}
public static class AnyMultiItemFilter implements ItemFilter {
public List<ItemFilter> filters;
public AnyMultiItemFilter(List<ItemFilter> filters) {
this.filters = filters;
}
public AnyMultiItemFilter() {
this(new LinkedList<>());
}
@Override
public boolean matches(ItemStack item) {
for (ItemFilter filter : filters) try {
if (filter != null && filter.matches(item)) return true;
} catch (Exception e) {
NEIClientConfig.logger
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
}
return false;
}
}
public static interface ItemsLoadedCallback {
public void itemsLoaded();
@@ -93,168 +207,6 @@ public class ItemList {
return filters;
}
public static final RestartableTask loadItems = new RestartableTask("NEI Item Loading") {
private void damageSearch(Item item, List<ItemStack> permutations) {
HashSet<String> damageIconSet = new HashSet<>();
for (int damage = 0; damage < 16; damage++) try {
ItemStack itemstack = new ItemStack(item, 1, damage);
IIcon icon = item.getIconIndex(itemstack);
String name = getTooltip(itemstack);
String s = name + "@" + (icon == null ? 0 : icon.hashCode());
if (damageIconSet.add(s)) {
permutations.add(itemstack);
}
} catch (TimeoutException t) {
throw t;
} catch (Throwable t) {
NEIServerUtils.logOnce(
t,
stackTraces,
"Ommiting " + item + ":" + damage + " " + item.getClass().getSimpleName(),
item.toString());
}
}
private String getTooltip(ItemStack stack) {
try {
return String.join("\n", stack.getTooltip(Minecraft.getMinecraft().thePlayer, false));
} catch (Throwable ignored) {}
return "";
}
private void updateOrdering(List<ItemStack> items) {
final Map<ItemStack, Integer> newOrdering = new HashMap<>();
ItemSorter.sort(items);
if (!CollapsibleItems.isEmpty()) {
final HashMap<Integer, Integer> groups = new HashMap<>();
int orderIndex = 0;
for (ItemStack stack : items) {
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
if (groupIndex == -1) {
newOrdering.put(stack, orderIndex++);
} else {
if (!groups.containsKey(groupIndex)) {
groups.put(groupIndex, orderIndex++);
}
newOrdering.put(stack, groups.get(groupIndex));
}
}
} else {
int orderIndex = 0;
for (ItemStack stack : items) {
newOrdering.put(stack, orderIndex++);
}
}
ItemList.ordering = newOrdering;
}
private List<ItemStack> getPermutations(Item item) {
final List<ItemStack> permutations = new LinkedList<>(ItemInfo.itemOverrides.get(item));
if (permutations.isEmpty()) {
item.getSubItems(item, null, permutations);
}
if (permutations.isEmpty()) {
damageSearch(item, permutations);
}
permutations.addAll(ItemInfo.itemVariants.get(item));
return permutations.stream()
.filter(
stack -> stack.getItem() != null && stack.getItem().delegate.name() != null
&& !ItemInfo.isHidden(stack))
.collect(Collectors.toList());
}
// Generate itemlist, permutations, orders, collapsibleitems, and informationhandler stacks
@Override
@SuppressWarnings("unchecked")
public void execute() {
if (!NEIClientConfig.isEnabled() || NEIClientUtils.mc().thePlayer == null) return;
ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
LayoutManager.itemsLoaded = true;
loadFinished = false;
SearchField.searchParser.clearCache();
ItemSorter.instance.ordering.clear();
CollapsibleItems.clearCache();
TooltipFilter.clearCache();
InformationHandler.clearCache();
List<ItemStack> items = new ArrayList<>();
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
ItemStackSet unique = new ItemStackSet();
StreamSupport.stream(((Iterable<Item>) Item.itemRegistry).spliterator(), true).forEach(item -> {
if (item == null || item.delegate.name() == null || erroredItems.contains(item)) return;
try {
timer.reset(item);
List<ItemStack> permutations = getPermutations(item);
timer.reset();
for (ItemStack stack : permutations) {
if (!unique.contains(stack)) {
synchronized (unique) {
unique.add(stack);
}
synchronized (items) {
items.add(stack);
}
CollapsibleItems.putItem(stack);
TooltipFilter.getSearchTooltip(stack);
InformationHandler.populateStacks(stack);
}
}
synchronized (itemMap) {
itemMap.putAll(item, permutations);
}
} catch (Throwable t) {
NEIServerConfig.logger.error("Removing item: {} from list.", item, t);
erroredItems.add(item);
}
});
int index = 0;
for (Item item : (Iterable<Item>) Item.itemRegistry) {
for (ItemStack stack : itemMap.get(item)) {
ItemSorter.instance.ordering.put(stack, index++);
}
}
if (interrupted()) return;
ItemList.items = items;
ItemList.itemMap = itemMap;
for (ItemsLoadedCallback callback : loadCallbacks) callback.itemsLoaded();
if (interrupted()) return;
updateOrdering(ItemList.items);
loadFinished = true;
SubsetWidget.updateHiddenItems();
ItemPanels.bookmarkPanel.load();
updateFilter.restart();
}
};
public static ForkJoinPool forkJoinPool;
static {
@@ -274,35 +226,12 @@ public class ItemList {
forkJoinPool = new ForkJoinPool(poolSize, factory, null, false);
}
public static final RestartableTask updateFilter = new RestartableTask("NEI Item Filtering") {
// The item list pipeline lives in ItemListLoader; these references are kept for backward compatibility.
public static final RestartableTask loadItems = ItemListLoader.loadItems;
@Override
public void execute() {
public static final RestartableTask refreshItems = ItemListLoader.refreshItems;
if (!loadFinished) return;
ItemFilter filter = getItemListFilter();
ArrayList<ItemStack> filtered;
try {
filtered = ItemList.forkJoinPool.submit(
() -> items.parallelStream().filter(filter::matches)
.collect(Collectors.toCollection(ArrayList::new)))
.get();
} catch (InterruptedException | ExecutionException e) {
filtered = new ArrayList<>();
e.printStackTrace();
stop();
}
if (interrupted()) return;
filtered.sort(Comparator.comparingInt(ItemList.ordering::get));
if (interrupted()) return;
ItemPanel.updateItemList(filtered);
}
};
public static final RestartableTask updateFilter = ItemListLoader.updateFilter;
/**
* @deprecated Use updateFilter.restart()
@@ -0,0 +1,385 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraftforge.fluids.IFluidContainerItem;
import com.google.common.base.Throwables;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import codechicken.nei.ItemList.ItemsLoadedCallback;
import codechicken.nei.ThreadOperationTimer.TimeoutException;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.recipe.RecipeHandlerRef;
import codechicken.nei.recipe.RepairRecipeHandler;
import codechicken.nei.search.TooltipFilter;
import cpw.mods.fml.common.FMLLog;
public class ItemListLoader {
private static final HashSet<Item> erroredItems = new HashSet<>();
private static final HashSet<String> stackTraces = new HashSet<>();
private static Map<ItemStack, Integer> ordering = new HashMap<>();
private ItemListLoader() {}
private static void damageSearch(Item item, List<ItemStack> permutations) {
final Set<String> damageIconSet = new HashSet<>();
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
for (int damage = 0; damage < 16; damage++) {
try {
final ItemStack itemstack = new ItemStack(item, 1, damage);
final IIcon icon = item.getIconIndex(itemstack);
final String key = itemstack.getDisplayName() + "@"
+ (icon == null ? 0 : icon.hashCode())
+ "@"
+ getInformation(itemstack, player);
if (damageIconSet.add(key)) {
permutations.add(itemstack);
}
} catch (TimeoutException t) {
throw t;
} catch (Throwable t) {
NEIServerUtils.logOnce(
t,
stackTraces,
"Ommiting " + item + ":" + damage + " " + item.getClass().getSimpleName(),
item.toString());
}
}
}
private static String getInformation(ItemStack stack, EntityPlayer player) {
try {
final List<String> tooltip = new ArrayList<>();
stack.getItem().addInformation(stack, player, tooltip, false);
return String.join("\n", tooltip);
} catch (Throwable ignored) {}
return "";
}
private static void updateOrdering(List<ItemStack> items) {
final Map<ItemStack, Integer> newOrdering = new HashMap<>();
if (!CollapsibleItems.isEmpty()) {
final HashMap<Integer, Integer> groups = new HashMap<>();
int orderIndex = 0;
for (ItemStack stack : items) {
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
if (groupIndex == -1) {
newOrdering.put(stack, orderIndex++);
} else {
if (!groups.containsKey(groupIndex)) {
groups.put(groupIndex, orderIndex++);
}
newOrdering.put(stack, groups.get(groupIndex));
}
}
} else {
int orderIndex = 0;
for (ItemStack stack : items) {
newOrdering.put(stack, orderIndex++);
}
}
ordering = newOrdering;
}
private static List<ItemStack> getPermutations(Item item) {
final List<ItemStack> permutations = new LinkedList<>(ItemInfo.itemOverrides.get(item));
if (permutations.isEmpty()) {
item.getSubItems(item, null, permutations);
}
if (permutations.isEmpty()) {
damageSearch(item, permutations);
}
permutations.addAll(ItemInfo.itemVariants.get(item));
return permutations.stream()
.filter(
stack -> stack.getItem() != null && stack.getItem().delegate.name() != null
&& !ItemInfo.isHidden(stack))
.collect(Collectors.toList());
}
private static void runChecked(ItemStack stack, Runnable action, String reason) {
int hashOld = 0;
if (stack.hasTagCompound()) {
hashOld = stack.stackTagCompound.hashCode();
}
action.run();
if (stack.hasTagCompound() && hashOld != stack.stackTagCompound.hashCode()) {
FMLLog.warning(
"NEI: Forced tag update with reason (" + reason + ") for " + stack + "(" + stack.getItem() + ")");
}
}
private static void forceTagCompoundInitialization(ItemStack stack) {
Item item = stack.getItem();
if (item == null) {
return;
}
if (item instanceof IFluidContainerItem fluidItem) {
runChecked(stack, () -> fluidItem.getFluid(stack), "getFluid");
}
runChecked(stack, () -> item.isDamaged(stack), "isDamaged");
runChecked(stack, () -> item.getDamage(stack), "getDamage");
runChecked(stack, () -> item.showDurabilityBar(stack), "showDurabilityBar");
runChecked(stack, () -> item.getAttributeModifiers(stack), "getAttributeModifiers");
runChecked(stack, () -> item.getDurabilityForDisplay(stack), "getDurabilityForDisplay");
runChecked(stack, () -> item.getItemStackLimit(stack), "getItemStackLimit");
runChecked(stack, () -> item.getToolClasses(stack), "getToolClasses");
runChecked(stack, () -> item.getUnlocalizedNameInefficiently(stack), "getUnlocalizedNameInefficiently");
runChecked(stack, () -> item.hasEffect(stack), "hasEffect");
runChecked(stack, () -> item.getDigSpeed(stack, Blocks.stone, 0), "getDigSpeed");
/*
* unused... for now runChecked(stack, () -> GameRegistry.getFuelValue(stack), "getFuelValue");
* runChecked(stack, () -> item.getUnlocalizedName(stack), "getUnlocalizedName"); runChecked(stack, () ->
* item.doesContainerItemLeaveCraftingGrid(stack), "doesContainerItemLeaveCraftingGrid"); runChecked(stack, ()
* -> item.getItemUseAction(stack), "getItemUseAction"); runChecked(stack, () ->
* item.getMaxItemUseDuration(stack), "getMaxItemUseDuration"); runChecked(stack, () ->
* item.getPotionEffect(stack), "getPotionEffect"); runChecked(stack, () -> item.isPotionIngredient(stack),
* "isPotionIngredient"); runChecked(stack, () -> item.getRarity(stack), "getRarity"); runChecked(stack, () ->
* item.hasCustomEntity(stack), "hasCustomEntity"); runChecked(stack, () -> item.getSmeltingExperience(stack),
* "getSmeltingExperience"); runChecked(stack, () -> item.getMaxDamage(stack), "getMaxDamage");
* runChecked(stack, () -> item.getDamage(stack), "getDamage"); runChecked(stack, stack::isItemDamaged,
* "isItemDamaged"); runChecked(stack, () -> item.hasEffect(stack, 0), "hasEffect"); runChecked(stack, () ->
* item.getItemEnchantability(stack), "getItemEnchantability"); runChecked(stack, () ->
* item.isBeaconPayment(stack), "isBeaconPayment"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
* runChecked(stack, () -> item.getDamage(stack), "getDamage"); runChecked(stack, () -> item.getDamage(stack),
* "getDamage");
*/
}
@SuppressWarnings("unchecked")
private static List<ItemStack> gatherItems(ThreadOperationTimer timer) {
ItemSorter.instance.ordering.clear();
List<ItemStack> items = new ArrayList<>();
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
ItemStackSet unique = new ItemStackSet();
final List<Item> registryItems = new ArrayList<>();
for (Item item : (Iterable<Item>) Item.itemRegistry) registryItems.add(item);
registryItems.parallelStream().forEach(item -> {
if (item == null || item.delegate.name() == null || erroredItems.contains(item)) return;
try {
timer.reset(item);
List<ItemStack> permutations = getPermutations(item);
timer.reset();
synchronized (itemMap) {
itemMap.putAll(item, permutations);
}
} catch (Throwable t) {
NEIServerConfig.logger.error(
"Removing item: {} from list.\n{}: {}",
item,
t.getMessage(),
Throwables.getStackTraceAsString(t));
erroredItems.add(item);
}
});
int index = 0;
for (Item item : (Iterable<Item>) Item.itemRegistry) {
for (ItemStack stack : itemMap.get(item)) {
ItemSorter.instance.ordering.put(stack, index++);
if (!unique.contains(stack)) {
unique.add(stack);
items.add(stack);
}
}
}
ItemList.itemMap = itemMap;
return items;
}
private static void buildItemCaches(List<ItemStack> items) {
SearchField.searchParser.clearCache();
TooltipFilter.clearCache();
InformationHandler.clearCache();
items.parallelStream().forEach(stack -> {
try {
forceTagCompoundInitialization(stack);
InformationHandler.populateStacks(stack);
} catch (Throwable t) {
NEIServerConfig.logger.error(
"Failed to build caches for: {}\n{}: {}",
stack,
t.getMessage(),
Throwables.getStackTraceAsString(t));
}
});
}
private static void indexCollapsibleItems(List<ItemStack> items) {
CollapsibleItems.clearCache();
items.parallelStream().forEach(CollapsibleItems::putItem);
}
public static final RestartableTask loadItems = new RestartableTask("NEI Item Loading") {
@Override
public void execute() {
if (!NEIClientConfig.isEnabled() || NEIClientUtils.mc().thePlayer == null) return;
final ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
LayoutManager.itemsLoaded = true;
ItemList.loadFinished = false;
warmTooltips.stop();
List<ItemStack> items = gatherItems(timer);
if (interrupted()) return;
buildItemCaches(items);
indexCollapsibleItems(items);
if (interrupted()) return;
ItemSorter.sort(items);
if (interrupted()) return;
ItemList.items = items;
for (ItemsLoadedCallback callback : ItemList.loadCallbacks) callback.itemsLoaded();
if (interrupted()) return;
updateOrdering(ItemList.items);
ItemList.loadFinished = true;
RepairRecipeHandler.findRepairRecipesOnceParallel();
RecipeHandlerRef.clearCache();
FavoriteRecipes.reload();
SubsetWidget.updateHiddenItems();
updateFilter.restart();
warmTooltips.restart();
loadBookmarks.restart();
loadHistory.restart();
}
};
public static final RestartableTask warmTooltips = new RestartableTask("NEI Tooltip Caching") {
@Override
public void execute() {
if (!ItemList.loadFinished) return;
// Use the common pool, not ItemList.forkJoinPool, so it does not queue behind updateFilter.
ItemList.items.parallelStream().forEach(stack -> {
if (interrupted()) return;
try {
TooltipFilter.getSearchTooltip(stack);
} catch (Throwable ignored) {}
});
}
};
public static final RestartableTask loadBookmarks = new RestartableTask("NEI Bookmark Loading") {
@Override
public void execute() {
ItemPanels.bookmarkPanel.load();
}
};
public static final RestartableTask loadHistory = new RestartableTask("NEI History Loading") {
@Override
public void execute() {
ItemPanels.itemPanel.historyPanel.load();
}
};
public static final RestartableTask refreshItems = new RestartableTask("NEI Item Reordering") {
@Override
public void execute() {
if (!ItemList.loadFinished) return;
final List<ItemStack> items = new ArrayList<>(ItemList.items);
indexCollapsibleItems(items);
if (interrupted()) return;
ItemSorter.sort(items);
if (interrupted()) return;
ItemList.items = items;
updateOrdering(ItemList.items);
FavoriteRecipes.reload();
SubsetWidget.updateHiddenItems();
updateFilter.restart();
loadBookmarks.restart();
loadHistory.restart();
}
};
public static final RestartableTask updateFilter = new RestartableTask("NEI Item Filtering") {
@Override
public void execute() {
if (!ItemList.loadFinished) return;
ItemFilter filter = ItemList.getItemListFilter();
ArrayList<ItemStack> filtered;
try {
filtered = ItemList.forkJoinPool.submit(
() -> ItemList.items.parallelStream().filter(filter::matches)
.collect(Collectors.toCollection(ArrayList::new)))
.get();
} catch (InterruptedException | ExecutionException e) {
filtered = new ArrayList<>();
e.printStackTrace();
stop();
}
if (interrupted()) return;
filtered.sort(Comparator.comparingInt(ordering::get));
if (interrupted()) return;
ItemPanel.updateItemList(filtered);
}
};
}
+262 -68
View File
@@ -1,8 +1,10 @@
package codechicken.nei;
import java.util.HashMap;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
@@ -15,21 +17,40 @@ import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityMobSpawner;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class ItemMobSpawner extends ItemBlock {
private static final Map<Integer, EntityLiving> entityHashMap = new HashMap<>();
private static final Map<Integer, String> IDtoNameMap = new HashMap<>();
public static int idPig;
private static boolean loaded;
private static final Map<Integer, EntityLiving> entityHashMap = new ConcurrentHashMap<>();
private static final Map<Integer, String> IDtoNameMap = new ConcurrentHashMap<>();
private static final Map<Class<? extends Entity>, String> ENTITY_CLASS_TO_NAME_CACHE = new ConcurrentHashMap<>();
public static int idPig = 90;
private static volatile boolean loaded = false;
// For asm
public static int placedX;
public static int placedY;
public static int placedZ;
public static void setLastPlacedPosition(int x, int y, int z) {
placedX = x;
placedY = y;
placedZ = z;
}
public static int[] getLastPlacedPosition() {
return new int[] { placedX, placedY, placedZ };
}
public ItemMobSpawner() {
super(Blocks.mob_spawner);
@@ -37,99 +58,272 @@ public class ItemMobSpawner extends ItemBlock {
MinecraftForgeClient.registerItemRenderer(this, new SpawnerRenderer());
}
/**
* These are ASM translated from BlockMobSpawner
*/
public static int placedX;
public static int placedY;
public static int placedZ;
@Override
public IIcon getIconFromDamage(int par1) {
public IIcon getIconFromDamage(int meta) {
return Blocks.mob_spawner.getBlockTextureFromSide(0);
}
public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int par7,
float par8, float par9, float par10) {
if (super.onItemUse(itemstack, entityplayer, world, x, y, z, par7, par8, par9, par10) && world.isRemote) {
TileEntityMobSpawner tileentitymobspawner = (TileEntityMobSpawner) world
.getTileEntity(placedX, placedY, placedZ);
if (tileentitymobspawner != null) {
setDefaultTag(itemstack);
String mobtype = IDtoNameMap.get(itemstack.getItemDamage());
if (mobtype != null) {
NEICPH.sendMobSpawnerID(placedX, placedY, placedZ, mobtype);
tileentitymobspawner.func_145881_a().setEntityName(mobtype);
@Override
public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int side,
float hitX, float hitY, float hitZ) {
int targetX = x;
int targetY = y;
int targetZ = z;
if (!world.getBlock(x, y, z).isReplaceable(world, x, y, z)) {
ForgeDirection direction = ForgeDirection.getOrientation(side);
targetX += direction.offsetX;
targetY += direction.offsetY;
targetZ += direction.offsetZ;
}
placedX = targetX;
placedY = targetY;
placedZ = targetZ;
String mobType = getMobTypeFromItemStack(itemstack);
boolean placed = super.onItemUse(itemstack, entityplayer, world, x, y, z, side, hitX, hitY, hitZ);
if (placed) {
TileEntity tileEntity = world.getTileEntity(targetX, targetY, targetZ);
if (tileEntity instanceof TileEntityMobSpawner spawner && mobType != null) {
spawner.func_145881_a().setEntityName(mobType);
if (!world.isRemote) {
world.markBlockForUpdate(targetX, targetY, targetZ);
NEICPH.sendMobSpawnerID(targetX, targetY, targetZ, mobType);
}
}
return true;
}
return false;
}
private String getMobTypeFromItemStack(ItemStack stack) {
int meta = getValidMetaData(stack);
return IDtoNameMap.get(meta);
}
private int getValidMetaData(ItemStack stack) {
int meta = stack.getItemDamage();
if (meta == 0 || !IDtoNameMap.containsKey(meta)) {
return idPig;
}
return meta;
}
@SideOnly(Side.CLIENT)
@Override
public void addInformation(ItemStack itemstack, EntityPlayer par2EntityPlayer, List<String> list, boolean par4) {
setDefaultTag(itemstack);
int meta = itemstack.getItemDamage();
if (meta == 0) {
meta = idPig;
public void addInformation(ItemStack itemstack, EntityPlayer player, List<String> tooltip, boolean advanced) {
int meta = getValidMetaData(itemstack);
EntityLiving entity = getEntity(meta);
String mobName = IDtoNameMap.get(meta);
if (entity != null && mobName != null) {
EnumChatFormatting color = (entity instanceof IMob) ? EnumChatFormatting.DARK_RED
: EnumChatFormatting.DARK_AQUA;
tooltip.add(color + mobName);
}
Entity e = getEntity(meta);
list.add(
(e instanceof IMob ? EnumChatFormatting.DARK_RED : EnumChatFormatting.DARK_AQUA)
+ IDtoNameMap.get(meta));
}
public static EntityLiving getEntity(int ID) {
EntityLiving e = entityHashMap.get(ID);
if (e == null) {
loadSpawners();
Class<?> clazz = EntityList.IDtoClassMapping.get(ID);
World world = NEIClientUtils.mc().theWorld;
try {
e = (EntityLiving) clazz.getConstructor(new Class[] { World.class }).newInstance(world);
} catch (Throwable t) {
if (clazz == null)
NEIClientConfig.logger.error("Null class for entity (" + ID + ", " + IDtoNameMap.get(ID));
else NEIClientConfig.logger.error("Error creating instance of entity: " + clazz.getName(), t);
e = getEntity(idPig);
}
entityHashMap.put(ID, e);
public static EntityLiving getEntity(int id) {
EntityLiving cached = entityHashMap.get(id);
if (cached != null) {
return cached;
}
return e;
loadSpawners();
Class<?> entityClass = EntityList.IDtoClassMapping.get(id);
World world = NEIClientUtils.mc() != null ? NEIClientUtils.mc().theWorld : null;
if (isInvalidEntityClass(entityClass)) {
return cacheFallback(id);
}
EntityLiving entity = createEntityInstance(entityClass, world, id);
if (entity != null) {
entityHashMap.put(id, entity);
}
return entity;
}
private static boolean isInvalidEntityClass(Class<?> entityClass) {
if (entityClass == null) {
return true;
}
int modifiers = entityClass.getModifiers();
if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers)) {
NEIClientConfig.logger.warn("Skipping abstract/interface entity class: {}", entityClass.getName());
return true;
}
if (!EntityLiving.class.isAssignableFrom(entityClass)) {
NEIClientConfig.logger.warn("Skipping non-EntityLiving class: {}", entityClass.getName());
return true;
}
return false;
}
@SuppressWarnings("unchecked")
private static EntityLiving createEntityInstance(Class<?> entityClass, World world, int id) {
if (entityClass == null || world == null) {
NEIClientConfig.logger.debug("World or entity class is null, using fallback for ID: {}", id);
return getFallbackEntity(id);
}
try {
Class<? extends EntityLiving> livingClass = (Class<? extends EntityLiving>) entityClass;
return livingClass.getConstructor(World.class).newInstance(world);
} catch (Throwable t) {
logEntityCreationError(entityClass, id, t);
return getFallbackEntity(id);
}
}
private static void logEntityCreationError(Class<?> entityClass, int id, Throwable t) {
String entityName = IDtoNameMap.getOrDefault(id, "unknown");
if (entityClass == null) {
NEIClientConfig.logger
.error("Failed to create entity instance: Class is null for ID {}, Name {}", id, entityName, t);
} else {
NEIClientConfig.logger.error(
"Failed to create instance of entity: Class {}, ID {}, Name {}",
entityClass.getName(),
id,
entityName,
t);
}
}
private static EntityLiving cacheFallback(int id) {
EntityLiving fallback = getFallbackEntity(id);
if (fallback != null) {
entityHashMap.put(id, fallback);
}
return fallback;
}
private static EntityLiving getFallbackEntity(int originalId) {
EntityLiving pig = entityHashMap.get(idPig);
if (pig == null) {
World world = NEIClientUtils.mc() != null ? NEIClientUtils.mc().theWorld : null;
if (world != null) {
try {
pig = new EntityPig(world);
entityHashMap.put(idPig, pig);
NEIClientConfig.logger.debug("Created fallback pig entity for ID: {}", originalId);
} catch (Exception e) {
NEIClientConfig.logger
.error("Failed to create fallback pig entity for original ID: {}", originalId, e);
}
} else {
NEIClientConfig.logger.warn("World is null, cannot create fallback entity for ID: {}", originalId);
}
}
return pig;
}
public static void clearEntityReferences() {
entityHashMap.clear();
}
private void setDefaultTag(ItemStack itemstack) {
if (!IDtoNameMap.containsKey(itemstack.getItemDamage())) itemstack.setItemDamage(idPig);
NEIClientConfig.logger.debug("Cleared all entity references from cache");
}
public static void loadSpawners() {
if (loaded) return;
loaded = true;
for (Map.Entry<Class<? extends Entity>, String> entry : EntityList.classToStringMapping.entrySet()) {
final Class<? extends Entity> clazz = entry.getKey();
if (EntityLiving.class.isAssignableFrom(clazz)) {
Integer id = (Integer) EntityList.classToIDMapping.get(clazz);
if (id == null) continue;
String name = entry.getValue();
if (name == null) continue;
if (name.equals("EnderDragon")) continue;
if (name.equals("Pig")) idPig = id;
if (clazz != EntityPig.class || name.equals("Pig")) {
IDtoNameMap.put(id, name);
if (loaded) {
return;
}
synchronized (ItemMobSpawner.class) {
try {
for (Map.Entry<Class<? extends Entity>, String> entry : EntityList.classToStringMapping.entrySet()) {
Class<? extends Entity> clazz = entry.getKey();
String name = entry.getValue();
if (shouldRegisterEntity(clazz, name)) {
Integer id = (Integer) EntityList.classToIDMapping.get(clazz);
if (id != null) {
IDtoNameMap.put(id, name);
ENTITY_CLASS_TO_NAME_CACHE.put(clazz, name);
if ("Pig".equals(name)) {
idPig = id;
NEIClientConfig.logger.debug("Set pig entity ID to: {}", id);
}
}
}
}
loaded = true;
NEIClientConfig.logger.info("Loaded {} spawner entity mappings", IDtoNameMap.size());
} catch (Exception e) {
NEIClientConfig.logger.error("Error loading spawners from entity list", e);
}
}
}
private static boolean shouldRegisterEntity(Class<? extends Entity> clazz, String name) {
if (clazz == null || name == null || name.trim().isEmpty()) {
return false;
}
if (!EntityLiving.class.isAssignableFrom(clazz)) {
return false;
}
if ("EnderDragon".equals(name) || "Giant".equals(name)) {
return false;
}
return true;
}
@Override
public void getSubItems(Item item, CreativeTabs tab, List<ItemStack> list) {
if (!NEIClientConfig.hasSMPCounterPart()) list.add(new ItemStack(item));
else for (int i : IDtoNameMap.keySet()) list.add(new ItemStack(item, 1, i));
if (!NEIClientConfig.hasSMPCounterPart()) {
list.add(new ItemStack(item));
} else {
loadSpawners();
for (int id : IDtoNameMap.keySet()) {
ItemStack stack = new ItemStack(item, 1, id);
list.add(stack);
}
}
}
public static String getEntityNameForMeta(int meta) {
loadSpawners();
return IDtoNameMap.getOrDefault(meta, IDtoNameMap.getOrDefault(idPig, "Pig"));
}
public static boolean isValidEntityMeta(int meta) {
loadSpawners();
return IDtoNameMap.containsKey(meta) || meta == 0;
}
public static Map<Integer, String> getRegisteredSpawnerEntities() {
loadSpawners();
return Collections.unmodifiableMap(IDtoNameMap);
}
public static boolean isEntityValidForSpawner(Class<? extends Entity> entityClass) {
if (entityClass == null) return false;
String name = ENTITY_CLASS_TO_NAME_CACHE.get(entityClass);
if (name == null) {
name = EntityList.classToStringMapping.get(entityClass);
}
return name != null && shouldRegisterEntity(entityClass, name);
}
}
+29 -27
View File
@@ -115,31 +115,29 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
return String.format("%d/%d", getPage(), Math.max(1, getNumPages()));
}
public Rectangle4i calculateBounds() {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
final int width = (gui.width - gui.xSize) / 2 - PADDING * 2;
final Rectangle4i bounds = new Rectangle4i(
(gui.width + gui.xSize) / 2 + PADDING,
PADDING,
(gui.width - 176) / 2 - PADDING * 2,
gui.height - PADDING * 2);
@Override
public Rectangle4i calculateBounds(GuiContainer gui) {
final int maxWidth = (gui.width - 176) / 2 - PADDING * 2;
final int maxHeight = gui.height - PADDING * 2;
final int freeSpace = (gui.width - gui.xSize) / 2 - PADDING * 2;
int paddingLeft = (int) Math
.ceil(bounds.w * NEIClientConfig.getSetting("world.panels.items.left").getIntValue() / 100000.0);
int paddingTop = (int) Math
.ceil(bounds.h * NEIClientConfig.getSetting("world.panels.items.top").getIntValue() / 100000.0);
int paddingRight = (int) Math
.ceil(bounds.w * NEIClientConfig.getSetting("world.panels.items.right").getIntValue() / 100000.0);
int paddingBottom = (int) Math
.ceil(bounds.h * NEIClientConfig.getSetting("world.panels.items.bottom").getIntValue() / 100000.0);
final int paddingLeft = (int) Math
.ceil(maxWidth * NEIClientConfig.getSetting("world.panels.items.left").getIntValue() / 100000.0);
final int paddingTop = (int) Math
.ceil(maxHeight * NEIClientConfig.getSetting("world.panels.items.top").getIntValue() / 100000.0);
final int paddingRight = (int) Math
.ceil(maxWidth * NEIClientConfig.getSetting("world.panels.items.right").getIntValue() / 100000.0);
final int paddingBottom = (int) Math
.ceil(maxHeight * NEIClientConfig.getSetting("world.panels.items.bottom").getIntValue() / 100000.0);
bounds.h = Math.max(ItemsGrid.SLOT_SIZE, bounds.h - paddingTop - paddingBottom);
bounds.y = bounds.y + Math.min(paddingTop, bounds.h - ItemsGrid.SLOT_SIZE);
final int width = Math.max(
ItemsGrid.SLOT_SIZE,
snapDownToMultiple(
Math.min(freeSpace - paddingRight, maxWidth - paddingLeft - paddingRight),
ItemsGrid.SLOT_SIZE));
final int height = Math.max(ItemsGrid.SLOT_SIZE, maxHeight - paddingTop - paddingBottom);
bounds.w = Math.max(ItemsGrid.SLOT_SIZE, Math.min(bounds.w - paddingLeft - paddingRight, width - paddingRight));
bounds.x = bounds.x + Math.max(0, width - bounds.w - paddingRight);
return bounds;
return new Rectangle4i(gui.width - width - PADDING - paddingRight, paddingTop + PADDING, width, height);
}
@Override
@@ -222,8 +220,7 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
}
@Override
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
List<String> currenttip) {
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> currenttip) {
if (!this.grid.forceExpand && !currenttip.isEmpty()) {
final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
@@ -237,7 +234,7 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
}
}
return super.handleItemTooltip(gui, itemstack, mousex, mousey, currenttip);
return super.handleItemTooltip(itemstack, mousex, mousey, currenttip);
}
@Override
@@ -258,7 +255,7 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
}
@Override
public Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> hotkeys) {
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (!this.grid.forceExpand && hoverSlot != null && hoverSlot.groupIndex >= 0 && hoverSlot.groupSize > 1) {
@@ -269,7 +266,12 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
NEIClientUtils.translate(message, hoverSlot.groupSize));
}
return hotkeys;
return super.handleHotkeys(mousex, mousey, hotkeys);
}
public boolean containsWithSubpanels(int px, int py) {
return contains(px, py) || NEIClientConfig.showHistoryPanelWidget() && this.historyPanel.contains(px, py)
|| NEIClientConfig.showCraftablesPanelWidget() && this.craftablesPanel.contains(px, py);
}
}
@@ -83,11 +83,7 @@ public class ItemQuantityField extends TextField {
}
if (amount == 0) {
amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
if (amount == 0) {
amount = StackInfo.itemStackToNBT(stack).hasKey("gtFluidName") ? 144 : stack.getMaxStackSize();
}
amount = NEIClientUtils.getScrollMultiplier(stack);
}
return StackInfo.withAmount(stack, amount);
@@ -46,8 +46,7 @@ public class ItemStackMap<T> {
}
public boolean equals(Object o) {
if (!(o instanceof StackMetaKey)) return false;
StackMetaKey t = (StackMetaKey) o;
if (!(o instanceof StackMetaKey t)) return false;
return damage == t.damage && Objects.equal(tag, t.tag);
}
}
@@ -218,8 +217,7 @@ public class ItemStackMap<T> {
public void put(ItemStack key, T value) {
if (key == null || key.getItem() == null) return;
DetailMap map = itemMap.get(key.getItem());
if (map == null) itemMap.put(key.getItem(), map = new DetailMap());
DetailMap map = itemMap.computeIfAbsent(key.getItem(), k -> new DetailMap());
map.put(key, value);
}
+36 -9
View File
@@ -42,7 +42,7 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
if (this.stack != null && this.availableAreaWidth >= SLOT_SIZE * 2
&& (NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled")
|| NEIClientConfig.isKeyHashDown("gui.itemzoom_hold"))
|| KeyManager.isKeyDown("itemzoom.hold"))
&& GuiContainerManager.shouldShowTooltip(NEIClientUtils.getGuiContainer())) {
final float inverseScaleFactor = 1.0f / this.scale;
final float screenScale = getScreenScale();
@@ -58,7 +58,7 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
GL11.glPopMatrix();
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
GuiContainerManager.enable2DRender();
if (NEIClientConfig.getBooleanSetting("inventory.itemzoom.showName")) {
@@ -74,7 +74,7 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
if (NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled")
&& NEIClientConfig.getBooleanSetting("inventory.itemzoom.helpText")) {
String keyName = NEIClientConfig.getKeyName("gui.itemzoom_toggle");
String keyName = KeyManager.getKeyName("itemzoom.toggle", NEIClientUtils.SHIFT_HASH);
if (keyName != null) {
String helpText = NEIClientUtils.translate("itemzoom.toggle", keyName);
@@ -111,8 +111,14 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
this.xPosition = (availableAreaWidth - this.scale * SLOT_SIZE) / 2;
this.availableAreaWidth = (int) (availableAreaWidth / screenScale);
if (availableAreaWidth / screenScale >= mouse.x) {
this.xPosition += gui.width * screenScale - availableAreaWidth;
final float rightSideShift = gui.width * screenScale - availableAreaWidth;
final boolean leftSideFree = availableAreaWidth / screenScale < mouse.x;
final boolean rightSideFree = mouse.x < gui.width - availableAreaWidth / screenScale;
final boolean showOnRight = leftSideFree && rightSideFree ? !preferLeftSide() : !leftSideFree;
if (showOnRight) {
this.xPosition += rightSideShift;
}
try {
@@ -128,6 +134,22 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
}
}
private boolean preferLeftSide() {
switch (NEIClientConfig.getIntSetting("inventory.itemzoom.preferredSide")) {
case 1:
return true;
case 2:
return false;
}
final int leftCount = NEIClientConfig.isBookmarkPanelHidden()
|| ItemPanels.bookmarkPanel.getGrid().getPerPage() == 0 ? 0 : ItemPanels.bookmarkPanel.getGrid().size();
final int rightCount = ItemPanels.itemPanel.getGrid().getPerPage() == 0 ? 0
: ItemPanels.itemPanel.getGrid().size();
return leftCount <= rightCount;
}
private float getPointSize(GuiContainer gui) {
final float screenScale = 1.0f / getScreenScale();
final float availableAreaWidth = (gui.width - (gui.xSize + gui.width) / 2) * screenScale;
@@ -197,25 +219,30 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
if (NEIClientConfig.isKeyHashDown("gui.itemzoom_toggle")) {
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isHashDown("itemzoom.toggle", NEIClientUtils.SHIFT_HASH)) {
NEIClientConfig.getSetting("inventory.itemzoom.enabled")
.setBooleanValue(!NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled"));
return true;
}
if (this.stack != null && NEIClientConfig.isKeyHashDown("gui.itemzoom_hold")) {
if (this.stack != null && KeyManager.isKeyDown("itemzoom.hold")) {
return true;
}
if (this.stack != null && NEIClientConfig.isKeyHashDown("gui.itemzoom_zoom_in")) {
if (this.stack != null && KeyManager.isKeyDown("itemzoom.zoom_in")) {
increaseZoom(gui);
previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled();
Keyboard.enableRepeatEvents(true);
return true;
}
if (this.stack != null && NEIClientConfig.isKeyHashDown("gui.itemzoom_zoom_out")) {
if (this.stack != null && KeyManager.isKeyDown("itemzoom.zoom_out")) {
decreaseZoom(gui);
previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled();
Keyboard.enableRepeatEvents(true);
+23 -24
View File
@@ -19,6 +19,7 @@ import org.lwjgl.opengl.GL30;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.INEIGuiHandler;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.StackInfo;
@@ -28,7 +29,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
protected static class ScreenCapture {
private long nextCacheRefresh = 0;
private Framebuffer framebuffer;
private final Framebuffer framebuffer;
public ScreenCapture() {
this.framebuffer = new Framebuffer(1, 1, true);
@@ -52,7 +53,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
}
public void captureScreen(Runnable callback, int mode) {
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
resetFramebuffer();
this.framebuffer.bindFramebuffer(false);
@@ -86,7 +87,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
this.framebuffer.framebufferClear();
}
// copy depth buffer from MC (fix Angelica)
// Copy stencil values (required by mui2)
OpenGlHelper.func_153171_g(GL30.GL_READ_FRAMEBUFFER, minecraft.getFramebuffer().framebufferObject);
OpenGlHelper.func_153171_g(GL30.GL_DRAW_FRAMEBUFFER, this.framebuffer.framebufferObject);
GL30.glBlitFramebuffer(
@@ -281,8 +282,6 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
protected boolean[] invalidSlotMap;
protected Label messageLabel = new Label(getMessageOnEmpty(), true);
protected ScreenCapture screenCapture = null;
public ArrayList<ItemStack> getItems() {
@@ -350,19 +349,24 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
}
public void setGridSize(int mleft, int mtop, int w, int h) {
final int newWidth = Math.max(0, w);
final int newHeight = Math.max(0, h);
final boolean changed = marginLeft != mleft || marginTop != mtop || width != newWidth || height != newHeight;
marginLeft = mleft;
marginTop = mtop;
width = Math.max(0, w);
height = Math.max(0, h);
width = newWidth;
height = newHeight;
columns = width / SLOT_SIZE;
rows = height / SLOT_SIZE;
paddingLeft = (width % SLOT_SIZE) / 2;
messageLabel.x = marginLeft + width / 2;
messageLabel.y = marginTop + height / 2;
if (changed) {
onGridChanged();
}
}
public void shiftPage(int shift) {
@@ -401,7 +405,10 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
}
public void refresh(GuiContainer gui) {
updateGuiOverlapSlots(gui);
final int gridRenderingCacheMode = getGridRenderingCacheMode();
if (gridRenderingCacheMode <= 0 || screenCapture == null || screenCapture.needRefresh(gridRenderingCacheMode)) {
updateGuiOverlapSlots(gui);
}
shiftPage(0);
}
@@ -414,11 +421,12 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
perPage = columns * rows;
if (gui != null) {
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
if (NEIClientConfig.optimizeGuiOverlapComputation()) {
checkGuiOverlap(gui, 0, columns - 2, 1);
checkGuiOverlap(gui, columns - 1, 1, -1);
checkGuiOverlap(gui, handlers, 0, columns - 2, 1);
checkGuiOverlap(gui, handlers, columns - 1, 1, -1);
} else {
checkGuiOverlap(gui, 0, columns, 1);
checkGuiOverlap(gui, handlers, 0, columns, 1);
}
}
@@ -427,7 +435,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
}
}
private void checkGuiOverlap(GuiContainer gui, int start, int end, int dir) {
private void checkGuiOverlap(GuiContainer gui, INEIGuiHandler[] handlers, int start, int end, int dir) {
boolean validColumn = false;
for (int c = start; c != end && (!NEIClientConfig.optimizeGuiOverlapComputation() || !validColumn); c += dir) {
@@ -437,7 +445,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
final int idx = columns * r + c;
if (idx >= 0 && idx < invalidSlotMap.length
&& !invalidSlotMap[idx]
&& GuiInfo.hideItemPanelSlot(gui, getSlotRect(r, c))) {
&& GuiInfo.hideItemPanelSlot(gui, getSlotRect(r, c), handlers)) {
invalidSlotMap[idx] = true;
validColumn = false;
perPage--;
@@ -566,12 +574,6 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
afterDrawItems(mousex, mousey, mouseContext);
}
public void setVisible() {
if (getItems().isEmpty() && getMessageOnEmpty() != null) {
LayoutManager.addWidget(messageLabel);
}
}
public boolean contains(int px, int py) {
final Rectangle4i rect = new Rectangle4i(
marginLeft + paddingLeft,
@@ -586,7 +588,4 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
return !isInvalidSlot(columns * getRowIndex(py) + getColumnIndex(px));
}
public String getMessageOnEmpty() {
return null;
}
}
@@ -11,6 +11,7 @@ import java.util.Set;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.guihook.GuiContainerManager;
@@ -69,7 +70,7 @@ public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot,
}
public ArrayList<ItemStack> newItems;
public ArrayList<ItemStack> rawItems;
public ArrayList<ItemStack> rawItems = new ArrayList<>();
protected List<ItemsPanelGridSlot> gridMask;
protected final Map<Integer, Integer> itemToGroup = new HashMap<>();
@@ -88,6 +89,10 @@ public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot,
public void refresh(GuiContainer gui) {
if (this.newItems != null) {
if (this.rawItems.size() != this.newItems.size()) {
this.page = 0;
}
this.groupItems.clear();
this.itemToGroup.clear();
this.forceExpand = false;
@@ -237,9 +242,18 @@ public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot,
Math.min((int) (color.getAlpha() + (255f / 5) * 2), 255));
}
@Override
public String getMessageOnEmpty() {
return ItemList.loadFinished ? null : NEIClientUtils.translate("itempanel.loading");
public void draw(int mousex, int mousey) {
if (ItemList.loadFinished) {
super.draw(mousex, mousey);
} else {
GuiDraw.drawStringC(
NEIClientUtils
.cropText(GuiDraw.fontRenderer, NEIClientUtils.translate("itempanel.loading"), width - 10),
marginLeft + width / 2,
marginTop + height / 2,
0xffffff,
true);
}
}
}
@@ -4,6 +4,7 @@ import static codechicken.lib.gui.GuiDraw.fontRenderer;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -17,60 +18,269 @@ import org.lwjgl.opengl.GL12;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.gui.GuiDraw.ITooltipLineHandler;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.NEIClientUtils.Alignment;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.ReadableNumberConverter;
public class ItemsTooltipLineHandler implements ITooltipLineHandler {
protected static final int SLOT_SIZE = 18;
protected static final int MAX_COLUMNS = 11;
protected static final int MARGIN_TOP = 2;
@FunctionalInterface
public interface AmountRenderer {
protected String label;
protected EnumChatFormatting labelColor = EnumChatFormatting.GRAY;
protected List<ItemStack> items;
protected int activeStackIndex = -1;
protected Dimension size = new Dimension();
protected boolean saveStackSize = false;
protected int columns = 0;
protected int count = 0;
protected int rows = 0;
protected int length = 0;
public ItemsTooltipLineHandler(String label, List<ItemStack> items) {
this(label, items, true, 5);
void draw(int x, int y, ItemStack stack, long amount);
}
public ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows) {
this.label = label;
this.items = groupingItemStacks(items);
this.saveStackSize = saveStackSize;
this.length = this.items.size();
public static class NoAmountRenderer implements AmountRenderer {
if (this.length > 0) {
this.columns = Math.min(MAX_COLUMNS, this.length);
this.rows = Math.min(maxRows, (int) Math.ceil((float) this.length / this.columns));
@Override
public void draw(int x, int y, ItemStack stack, long amount) {
GuiContainerManager.drawItem(x, y, stack, true, "");
}
this.size.width = Math.max(this.columns * SLOT_SIZE, fontRenderer.getStringWidth(this.label) + 15);
this.size.height = this.rows * SLOT_SIZE + fontRenderer.FONT_HEIGHT + 2 + MARGIN_TOP;
protected static void drawAmountText(String text, Rectangle4i rect, int color, Alignment alignment) {
NEIClientUtils.drawNEIOverlayText(text, rect, 1, color, true, alignment);
}
}
this.count = Math.min(
this.length,
Math.min(
this.columns * this.rows,
this.length > MAX_COLUMNS * maxRows ? (MAX_COLUMNS * maxRows) : Integer.MAX_VALUE));
public static class TotalAmountRenderer extends NoAmountRenderer {
if (this.items.size() > this.count) {
String text = "+" + (this.items.size() - this.count);
this.count -= (int) Math.ceil((float) (fontRenderer.getStringWidth(text) - 2) / SLOT_SIZE);
private static class AmountText {
public final String text;
public final Alignment alignment;
public AmountText(String text, Alignment alignment) {
this.text = text;
this.alignment = alignment;
}
}
private final Map<ItemStack, AmountText> cache = new IdentityHashMap<>();
@Override
public void draw(int x, int y, ItemStack stack, long amount) {
super.draw(x, y, stack, amount);
drawAmount(new Rectangle4i(x, y, SLOT_SIZE - 2, SLOT_SIZE - 2), stack, amount);
}
protected void drawAmount(Rectangle4i rect, ItemStack stack, long amount) {
final AmountText amountText = this.cache.computeIfAbsent(stack, key -> {
final boolean isFluidDisplay = StackInfo.isFluidDisplayItem(key);
final String text = formatAmount(key, amount);
return new AmountText(
isFluidDisplay && !text.isEmpty() ? text + "L" : text,
isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
});
if (!amountText.text.isEmpty()) {
drawAmountText(amountText.text, rect, 0xFFFFFF, amountText.alignment);
}
}
protected String formatAmount(ItemStack stack, long amount) {
return amount == 0 ? "" : ReadableNumberConverter.INSTANCE.toWideReadableForm(amount);
}
}
public static class StacksAmountRenderer extends NoAmountRenderer {
private static class StacksText {
public final String stacks;
public final String remainder;
public final int color;
public StacksText(String stacks, String remainder, int color) {
this.stacks = stacks;
this.remainder = remainder;
this.color = color;
}
}
private final Map<ItemStack, StacksText> cache = new IdentityHashMap<>();
@Override
public void draw(int x, int y, ItemStack stack, long amount) {
super.draw(x, y, stack, amount);
drawAmount(new Rectangle4i(x, y, SLOT_SIZE - 2, SLOT_SIZE - 2), stack, amount);
}
protected void drawAmount(Rectangle4i rect, ItemStack stack, long amount) {
final StacksText stacksText = this.cache.computeIfAbsent(stack, key -> formatAmount(key, amount));
if (!stacksText.stacks.isEmpty()) {
drawAmountText(stacksText.stacks, rect, stacksText.color, Alignment.TopLeft);
}
if (!stacksText.remainder.isEmpty()) {
drawAmountText(stacksText.remainder, rect, stacksText.color, Alignment.BottomRight);
}
}
private StacksText formatAmount(ItemStack stack, long amount) {
final long stackSize = StackInfo.isFluidDisplayItem(stack) ? FLUID_STACK_SIZE
: Math.max(1, stack.getMaxStackSize());
final long stacks = amount / stackSize;
final long remainder = amount % stackSize;
final String prefix = getPrefix(stack);
return new StacksText(
stacks > 0 ? prefix + "x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(stacks) : "",
remainder > 0
? (stacks > 0 ? "+" : prefix)
+ ReadableNumberConverter.INSTANCE.toWideReadableForm(remainder)
: "",
getColor(stack));
}
protected String getPrefix(ItemStack stack) {
return "";
}
protected int getColor(ItemStack stack) {
return 0xFFFFFF;
}
}
protected static final int SLOT_SIZE = 18;
protected static final int ICON_SIZE = 16;
protected static final int ICON_OFFSET = 1;
protected static final int MAX_COLUMNS = 11;
protected static final int MARGIN_TOP = 2;
protected static final int LABEL_MARGIN = 15;
protected static final int NAME_MARGIN = 2;
protected static final int DEFAULT_MAX_ROWS = 5;
protected static final int FLUID_STACK_SIZE = 144;
protected final List<ItemStack> items;
protected final List<Long> amounts = new ArrayList<>();
protected final List<String> names = new ArrayList<>();
protected AmountRenderer amountRenderer;
protected String label;
protected EnumChatFormatting labelColor = EnumChatFormatting.GRAY;
protected int activeStackIndex = -1;
protected Dimension size = new Dimension();
protected int maxNamedRows = Integer.MAX_VALUE;
protected int length = 0;
protected int rows = 0;
protected int nameRows = 0;
protected int gridColumns = 0;
protected int gridCount = 0;
public static ItemsTooltipLineHandler of(String label, List<ItemStack> items, int maxRows, int maxNamedRows) {
return new ItemsTooltipLineHandler(label, items, true, maxRows, maxNamedRows);
}
public ItemsTooltipLineHandler(String label, List<ItemStack> items) {
this(label, items, true, DEFAULT_MAX_ROWS, 0);
}
public ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows) {
this(label, items, saveStackSize, maxRows, 0);
}
protected ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows,
int maxNamedRows) {
this.label = label;
this.items = groupingItemStacks(items);
this.amountRenderer = saveStackSize ? new TotalAmountRenderer() : new NoAmountRenderer();
this.length = this.items.size();
this.maxNamedRows = maxNamedRows;
setMaxRows(maxRows);
}
protected static int headerHeight() {
return fontRenderer.FONT_HEIGHT + 2 + MARGIN_TOP;
}
public ItemsTooltipLineHandler setMaxRows(int maxRows) {
if (this.length == 0) {
return this;
}
this.names.clear();
this.nameRows = 0;
if (!layoutWithNames(maxRows)) {
layoutAsGrid(maxRows);
}
this.size.height = this.rows * SLOT_SIZE + headerHeight();
return this;
}
public int getRows() {
return this.rows;
}
protected boolean layoutWithNames(int maxRows) {
final int maxNames = Math.min(this.maxNamedRows, this.length <= maxRows ? this.length : maxRows - 1);
final int[] lineWidth = new int[maxNames + 1];
lineWidth[0] = labelWidth();
for (int index = 0; index < maxNames; index++) {
final String name = this.items.get(index).getDisplayName();
this.names.add(name);
lineWidth[index + 1] = Math
.max(lineWidth[index], SLOT_SIZE + NAME_MARGIN + fontRenderer.getStringWidth(name));
}
for (int named = maxNames; named >= 0; named--) {
final int rest = this.length - named;
final int width = Math.max(lineWidth[named], Math.min(rest, MAX_COLUMNS) * SLOT_SIZE);
final int columns = Math.max(1, width / SLOT_SIZE);
final int restRows = (int) Math.ceil((float) rest / columns);
if (named + restRows <= maxRows) {
this.nameRows = named;
this.gridColumns = columns;
this.gridCount = rest;
this.rows = named + restRows;
this.size.width = width;
return true;
}
}
this.names.clear();
return false;
}
protected void layoutAsGrid(int maxRows) {
this.gridColumns = Math.min(MAX_COLUMNS, this.length);
this.rows = Math.min(maxRows, (int) Math.ceil((float) this.length / this.gridColumns));
this.gridCount = Math.min(this.length, this.gridColumns * this.rows);
this.size.width = Math.max(this.gridColumns * SLOT_SIZE, labelWidth());
if (this.gridCount < this.length) {
this.gridCount -= reservedColumns(this.length - this.gridCount);
}
}
protected int labelWidth() {
return fontRenderer.getStringWidth(this.label) + LABEL_MARGIN;
}
protected int reservedColumns(int hidden) {
return (int) Math.ceil((float) (fontRenderer.getStringWidth("+" + hidden) - 2) / SLOT_SIZE);
}
public ItemsTooltipLineHandler setAmountRenderer(AmountRenderer amountRenderer) {
this.amountRenderer = amountRenderer;
return this;
}
public void setActiveStack(ItemStack activeStack) {
final ItemStack realStack = items.stream().filter(stack -> StackInfo.equalItemAndNBT(stack, activeStack, true))
final ItemStack realStack = items.stream().filter(stack -> NEIClientUtils.areStacksSameType(stack, activeStack))
.findFirst().orElse(null);
this.activeStackIndex = items.indexOf(realStack);
}
@@ -79,6 +289,10 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
return this.activeStackIndex == -1 ? null : this.items.get(this.activeStackIndex);
}
public List<ItemStack> getItems() {
return this.items;
}
public boolean isEmpty() {
return this.items.isEmpty();
}
@@ -92,6 +306,11 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
this.labelColor = color;
}
public void setLabel(String label) {
this.label = label;
this.size.width = Math.max(this.size.width, labelWidth());
}
@Override
public void draw(int x, int y) {
if (this.length == 0) return;
@@ -100,70 +319,89 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
fontRenderer.drawStringWithShadow(this.labelColor + this.label + ":", x, y, 0);
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushMatrix();
RenderHelper.enableGUIStandardItemLighting();
GL11.glScaled(1, 1, 3);
GL11.glTranslatef(x, y + fontRenderer.FONT_HEIGHT + 2, 0);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
int indexShift = 0;
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
RenderHelper.enableGUIStandardItemLighting();
if (this.activeStackIndex != -1) {
indexShift = Math.max(0, Math.min(this.items.size() - this.count, this.activeStackIndex - this.count + 2));
final int xTranslation = x;
final int yTranslation = y + fontRenderer.FONT_HEIGHT + 2;
final int zTranslation = GuiContainerManager.TOOLTIP_Z_OFFSET;
GL11.glTranslatef(xTranslation, yTranslation, zTranslation);
final int gridShift = gridIndexShift();
for (int index = 0; index < this.nameRows; index++) {
drawSlot(0, index * SLOT_SIZE, index);
}
for (int index = 0; index < this.count && index + indexShift < this.items.size(); index++) {
ItemStack drawStack = this.items.get(index + indexShift);
int col = index % this.columns;
int row = index / this.columns;
for (int index = 0; index < this.gridCount; index++) {
drawSlot(
(index % this.gridColumns) * SLOT_SIZE,
(this.nameRows + index / this.gridColumns) * SLOT_SIZE,
this.nameRows + gridShift + index);
}
String stackSize = !this.saveStackSize || drawStack.stackSize == 0 ? ""
: ReadableNumberConverter.INSTANCE.toWideReadableForm(drawStack.stackSize);
NEIClientUtils.gl2DRenderContext(() -> {
final int textShift = ICON_OFFSET + Math.round((ICON_SIZE - fontRenderer.FONT_HEIGHT) / 2f);
if (this.activeStackIndex == index + indexShift) {
NEIClientUtils.gl2DRenderContext(
() -> GuiDraw.drawRect(col * SLOT_SIZE - 1, row * SLOT_SIZE - 1, 18, 18, 0x66555555));
for (int index = 0; index < this.nameRows; index++) {
fontRenderer.drawStringWithShadow(
EnumChatFormatting.GRAY + this.names.get(index),
SLOT_SIZE + NAME_MARGIN,
index * SLOT_SIZE + textShift,
0xFFFFFF);
}
drawItem(col * SLOT_SIZE, row * SLOT_SIZE, drawStack, stackSize);
}
final int hidden = this.length - this.nameRows - this.gridCount;
if (this.count < this.items.size()) {
final String text = "+" + (this.items.size() - this.count);
NEIClientUtils.gl2DRenderContext(() -> {
if (hidden > 0) {
final String text = "+" + hidden;
fontRenderer.drawStringWithShadow(
text,
MAX_COLUMNS * SLOT_SIZE - fontRenderer.getStringWidth(text) - 2,
(this.rows - 1) * SLOT_SIZE + (SLOT_SIZE - fontRenderer.FONT_HEIGHT) / 2,
0xee555555);
});
}
EnumChatFormatting.GRAY + text,
this.size.width - fontRenderer.getStringWidth(text) - 2,
(this.rows - 1) * SLOT_SIZE + textShift,
0xFFFFFF);
}
});
GL11.glPopMatrix();
GL11.glTranslatef(-xTranslation, -yTranslation, -zTranslation);
GL11.glPopAttrib();
}
protected void drawItem(int x, int y, ItemStack drawStack, String stackSize) {
GuiContainerManager.drawItem(x, y, drawStack, true, stackSize);
protected int gridIndexShift() {
if (this.nameRows > 0 || this.activeStackIndex == -1) {
return 0;
}
return Math.max(0, Math.min(this.length - this.gridCount, this.activeStackIndex - this.gridCount + 2));
}
protected void drawSlot(int x, int y, int index) {
if (this.activeStackIndex == index) {
NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(x, y, SLOT_SIZE, SLOT_SIZE, 0x66555555));
}
drawStackWithAmount(x, y, index);
}
protected void drawStackWithAmount(int x, int y, int index) {
this.amountRenderer.draw(x + ICON_OFFSET, y + ICON_OFFSET, this.items.get(index), this.amounts.get(index));
}
private List<ItemStack> groupingItemStacks(List<ItemStack> items) {
final List<ItemStack> result = new ArrayList<>();
for (Map.Entry<NBTTagCompound, Long> entry : ItemStackAmount.of(items).entrySet()) {
final ItemStack stack = StackInfo.loadFromNBT(entry.getKey(), Math.max(0, entry.getValue()));
if (entry.getKey().hasKey("gtFluidName")) {
stack.stackSize = 0;
}
result.add(stack);
result.add(StackInfo.loadFromNBT(entry.getKey(), 0));
this.amounts.add(Math.max(0, entry.getValue()));
}
if (result.isEmpty()) {
result.addAll(items);
for (ItemStack stack : items) {
result.add(stack);
this.amounts.add(0L);
}
}
return result;
+171 -21
View File
@@ -2,14 +2,22 @@ package codechicken.nei;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.Map;
import java.util.StringJoiner;
import java.util.function.BooleanSupplier;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import codechicken.nei.util.NEIKeyboardUtils;
import codechicken.nei.util.NEIMouseUtils;
import cpw.mods.fml.client.registry.ClientRegistry;
/**
* Good old down/held/up keystate tracker
* Registers NEI key bindings as vanilla {@link KeyBinding}s and exposes their state to the rest of NEI.
*/
public class KeyManager {
@@ -18,31 +26,173 @@ public class KeyManager {
public void tickKeyStates();
}
public static class KeyState {
public static LinkedList<IKeyStateTracker> trackers = new LinkedList<>();
private static final Map<String, KeyBinding> keyBindings = new HashMap<>();
private static final Map<String, String> keyAliases = new HashMap<>();
private static volatile boolean pendingOptionsReload = false;
private static int clickedButton = -1;
public boolean down;
public boolean held;
public boolean up;
static {
// Aliases for legacy keybinds
keyAliases.put("gui.recipe", "recipe.recipe");
keyAliases.put("gui.usage", "recipe.usage");
keyAliases.put("gui.hide_bookmarks", "bookmark.hide");
}
public static HashMap<String, KeyState> keyStates = new HashMap<>();
public static LinkedList<IKeyStateTracker> trackers = new LinkedList<>();
public static KeyBinding registerKeyBinding(String ident, int defaultKey) {
return keyBindings.computeIfAbsent(ident, id -> {
final KeyBinding binding = new KeyBinding(
"nei.options.keys." + id,
NEIKeyboardUtils.unhash(defaultKey),
categoryFor(id));
ClientRegistry.registerKeyBinding(binding);
return binding;
});
}
public static KeyBinding getKeyBinding(String ident) {
return keyBindings.get(keyAliases.getOrDefault(ident, ident));
}
public static boolean isRegistered(String description) {
for (KeyBinding binding : keyBindings.values()) {
if (binding.getKeyDescription().equals(description)) {
return true;
}
}
return false;
}
public static int getKeyCode(String ident) {
final KeyBinding binding = getKeyBinding(ident);
return binding == null ? Keyboard.KEY_NONE : binding.getKeyCode();
}
public static boolean isKeyDown(String ident) {
final KeyBinding binding = getKeyBinding(ident);
if (binding != null) {
final int keyCode = binding.getKeyCode();
if (keyCode < 0) {
final int button = keyCode + 100;
return Mouse.isButtonDown(button) || button == clickedButton;
} else if (keyCode > Keyboard.KEY_NONE && keyCode < Keyboard.KEYBOARD_SIZE) {
return Keyboard.isKeyDown(keyCode);
}
}
return false;
}
/**
* Whether any NEI key binding is assigned to the given mouse button.
*/
public static boolean isMouseBound(int button) {
for (KeyBinding binding : keyBindings.values()) {
if (binding.getKeyCode() == button - 100) {
return true;
}
}
return false;
}
/**
* Runs a hotkey handler for a click of the given mouse button if the button is bound to any NEI key binding. The
* button is reported as down by {@link #isKeyDown} for the duration of the call, because the LWJGL button state can
* already be released when a quick click is processed.
*
* @return true if the click was consumed by the handler
*/
public static boolean handleMouseHotkey(int button, BooleanSupplier handler) {
if (!isMouseBound(button)) {
return false;
}
final int previous = clickedButton;
clickedButton = button;
try {
return handler.getAsBoolean();
} finally {
clickedButton = previous;
}
}
public static boolean isHashDown(String ident) {
return isHashDown(ident, 0);
}
public static boolean isHashDown(String ident, int modifierMask) {
if (!isKeyDown(ident)) {
return false;
}
if (NEIKeyboardUtils.isHashKey(getKeyCode(ident))) {
return true;
}
return ((modifierMask & NEIKeyboardUtils.CTRL_HASH) != 0) == NEIClientUtils.controlKey()
&& ((modifierMask & NEIKeyboardUtils.SHIFT_HASH) != 0) == NEIClientUtils.shiftKey()
&& ((modifierMask & NEIKeyboardUtils.ALT_HASH) != 0) == NEIClientUtils.altKey();
}
public static String getKeyName(String ident) {
return getKeyName(ident, 0);
}
public static String getKeyName(String ident, int meta) {
final int keyCode = getKeyCode(ident);
return keyCode == Keyboard.KEY_NONE ? null : NEIKeyboardUtils.getKeyName(keyCode, meta);
}
public static String getKeyName(String ident, int meta, int mouseBind) {
final int keyCode = getKeyCode(ident);
return keyCode == Keyboard.KEY_NONE && mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? null
: getKeyName(keyCode, meta, mouseBind);
}
public static String getKeyName(int keyBind, int mouseBind) {
final int keyCode = NEIKeyboardUtils.unhash(keyBind);
return getKeyName(keyCode, keyBind - keyCode, mouseBind);
}
private static String getKeyName(int keyCode, int hash, int mouseBind) {
StringJoiner keyText = new StringJoiner(" + ");
String keyHash = keyCode == Keyboard.KEY_NONE && hash == 0 ? "" : NEIKeyboardUtils.getKeyName(keyCode, hash);
String mouseHash = mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? "" : NEIMouseUtils.getKeyName(mouseBind);
if (!keyHash.isEmpty()) {
keyText.add(keyHash);
}
if (!mouseHash.isEmpty()) {
keyText.add(mouseHash);
}
return keyText.toString();
}
public static boolean isPressed(String ident) {
final KeyBinding binding = getKeyBinding(ident);
return binding != null && binding.isPressed();
}
private static String categoryFor(String ident) {
final int dot = ident.indexOf('.');
return dot < 0 ? "nei.options.keys" : "nei.options.keys." + ident.substring(0, dot);
}
public static void requestOptionsReload() {
pendingOptionsReload = true;
}
public static void tickKeyStates() {
for (Entry<String, KeyState> entry : keyStates.entrySet()) {
final int keyCode = NEIKeyboardUtils.unhash(NEIClientConfig.getKeyBinding(entry.getKey()));
final boolean down = keyCode < Keyboard.KEYBOARD_SIZE ? Keyboard.isKeyDown(keyCode) : null;
final KeyState state = entry.getValue();
if (down) {
state.down = !state.held;
state.up = false;
} else {
state.up = state.held;
state.down = false;
}
state.held = down;
if (pendingOptionsReload) {
pendingOptionsReload = false;
Minecraft.getMinecraft().gameSettings.loadOptions();
}
for (IKeyStateTracker tracker : trackers) tracker.tickKeyStates();
@@ -43,6 +43,7 @@ import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.gui.inventory.GuiContainerCreative;
import net.minecraft.client.renderer.InventoryEffectRenderer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -68,6 +69,7 @@ import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.AutoCraftingManager;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.RecipeCatalysts;
import codechicken.nei.recipe.debug.DebugHandlerWidget;
public class LayoutManager implements IContainerInputHandler, IContainerTooltipHandler, IContainerDrawHandler,
IContainerObjectHandler, IKeyStateTracker {
@@ -102,7 +104,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
public static Button delete;
public static ButtonCycled gamemode;
public static Button rain;
public static Button magnet;
public static ButtonCycled magnet;
public static Button[] timeButtons = new Button[4];
public static Button heal;
@@ -117,11 +119,6 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
*/
public static boolean itemsLoaded = false;
/**
* Not present, Present
*/
public static Image[] itemPresenceOverlays = new Image[2];
public static void load() {
API.addLayoutStyle(0, new LayoutStyleMinecraft());
@@ -223,22 +220,40 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
if (NEIClientConfig.isKeyHashDown("gui.hide")) {
if (isEnabled() && !isHidden()) {
for (Widget widget : controlWidgets) {
if (inputFocused == null) {
widget.lastKeyTyped(keyID, keyChar);
}
}
}
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isKeyDown("gui.hide")) {
toggleBooleanSetting("inventory.hidden");
return true;
}
if (NEIClientConfig.isKeyHashDown("gui.hide_bookmarks")) {
if (KeyManager.isKeyDown("bookmark.hide")) {
toggleBooleanSetting("inventory.bookmarks.enabled");
return true;
}
if (NEIClientConfig.isKeyHashDown("gui.next_tooltip") && GuiContainerManager.incrementTooltipPage()) {
if (KeyManager.isKeyDown("gui.next_tooltip") && GuiContainerManager.incrementTooltipPage()) {
return true;
}
if (isEnabled() && !isHidden()) {
for (Widget widget : controlWidgets) if (inputFocused == null) widget.lastKeyTyped(keyID, keyChar);
if (isEnabled() && !isHidden() && inputFocused == null) {
for (Widget widget : controlWidgets) {
if (widget.onHotkeyPressed()) {
return true;
}
}
}
return false;
@@ -291,19 +306,6 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
}
}
@Override
public void postRenderTooltips(GuiContainer gui, int mousex, int mousey, List<String> tooltip) {
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
final Widget focused = getWidgetUnderMouse(mousex, mousey);
if (focused != null) {
focused.postDrawTooltips(mousex, mousey, tooltip);
}
}
}
@Override
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
@@ -354,6 +356,10 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
final Widget focused = getWidgetUnderMouse(mousex, mousey);
if (focused != null) {
currenttip = focused.handleItemTooltip(itemstack, mousex, mousey, currenttip);
}
if (focused instanceof IContainerTooltipHandler tip) {
currenttip = tip.handleItemTooltip(gui, itemstack, mousex, mousey, currenttip);
}
@@ -368,9 +374,14 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
final Widget focused = getWidgetUnderMouse(mousex, mousey);
if (focused instanceof IContainerTooltipHandler tip) {
hotkeys = tip.handleHotkeys(gui, mousex, mousey, hotkeys);
if (focused != null) {
hotkeys = focused.handleHotkeys(mousex, mousey, hotkeys);
if (focused instanceof IContainerTooltipHandler tip) {
hotkeys = tip.handleHotkeys(gui, mousex, mousey, hotkeys);
}
}
}
return hotkeys;
@@ -600,7 +611,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
return getStateTip("rain", state);
}
};
magnet = new Button() {
magnet = new ButtonCycled(2) {
@Override
public boolean onButtonPress(boolean rightclick) {
@@ -783,9 +794,10 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
addWidget(itemZoom);
if (visiblity.showSubsetDropdown) {
// Bookmarks or Subset/dropdown
addWidget(dropDown);
}
addWidget(DebugHandlerWidget.instance);
}
public static LayoutStyle getLayoutStyle(int id) {
@@ -809,6 +821,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (!isEnabled()) return;
AutoFocusWidget.instance.guiTick();
for (Widget widget : drawWidgets) widget.update();
}
@@ -905,29 +919,39 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
final int ty2 = ty + 20 - h2 - te;
final int tx2 = 200 - w2 - te;
final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_TEXTURE_2D);
changeTexture("textures/gui/widgets.png");
drawTexturedModalRect(x, y, tx1, ty1, w1, h1); // top left
drawTexturedModalRect(x, y2, tx1, ty2, w1, h2); // bottom left
final Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
addTexturedModalRect(tessellator, x, y, tx1, ty1, w1, h1); // top left
addTexturedModalRect(tessellator, x, y2, tx1, ty2, w1, h2); // bottom left
for (int tile = 0; tile < wTiles; tile++) {
final int tileX = x + w1 + 50 * tile;
drawTexturedModalRect(tileX, y, tx3, ty1, 50, h1); // top
drawTexturedModalRect(tileX, y2, tx3, ty2, 50, h2); // bottom
addTexturedModalRect(tessellator, tileX, y, tx3, ty1, 50, h1); // top
addTexturedModalRect(tessellator, tileX, y2, tx3, ty2, 50, h2); // bottom
}
drawTexturedModalRect(x2, y, tx2, ty1, w2, h1); // top right
drawTexturedModalRect(x2, y2, tx2, ty2, w2, h2); // bottom right
addTexturedModalRect(tessellator, x2, y, tx2, ty1, w2, h1); // top right
addTexturedModalRect(tessellator, x2, y2, tx2, ty2, w2, h2); // bottom right
tessellator.draw();
if (!is2DTexture) GL11.glDisable(GL11.GL_TEXTURE_2D);
}
public static void drawItemPresenceOverlay(int slotX, int slotY, boolean isPresent, boolean slotHighlight) {
if (slotHighlight) {
NEIClientUtils.gl2DRenderContext(() -> drawRect(slotX, slotY, 16, 16, isPresent ? 0x8000AA00 : 0x80AA0000));
} else {
Image icon = itemPresenceOverlays[isPresent ? 1 : 0];
drawIcon(slotX + 16 - icon.width, slotY + 16 - icon.height, icon);
}
private static void addTexturedModalRect(Tessellator tessellator, int x, int y, int textureX, int textureY,
int width, int height) {
final double z = GuiDraw.gui.getZLevel();
final double u = textureX / 256D;
final double v = textureY / 256D;
final double maxU = (textureX + width) / 256D;
final double maxV = (textureY + height) / 256D;
tessellator.addVertexWithUV(x, y + height, z, u, maxV);
tessellator.addVertexWithUV(x + width, y + height, z, maxU, maxV);
tessellator.addVertexWithUV(x + width, y, z, maxU, v);
tessellator.addVertexWithUV(x, y, z, u, v);
}
public static LayoutManager instance() {
@@ -938,12 +962,12 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
public void tickKeyStates() {
if (Minecraft.getMinecraft().currentScreen != null) return;
if (KeyManager.keyStates.get("world.dawn").down) timeButtons[0].onButtonPress(false);
if (KeyManager.keyStates.get("world.noon").down) timeButtons[1].onButtonPress(false);
if (KeyManager.keyStates.get("world.dusk").down) timeButtons[2].onButtonPress(false);
if (KeyManager.keyStates.get("world.midnight").down) timeButtons[3].onButtonPress(false);
if (KeyManager.keyStates.get("world.rain").down) rain.onButtonPress(false);
if (KeyManager.keyStates.get("world.heal").down) heal.onButtonPress(false);
if (KeyManager.keyStates.get("world.creative").down) gamemode.onButtonPress(false);
if (KeyManager.isPressed("world.dawn")) timeButtons[0].onButtonPress(false);
if (KeyManager.isPressed("world.noon")) timeButtons[1].onButtonPress(false);
if (KeyManager.isPressed("world.dusk")) timeButtons[2].onButtonPress(false);
if (KeyManager.isPressed("world.midnight")) timeButtons[3].onButtonPress(false);
if (KeyManager.isPressed("world.rain")) rain.onButtonPress(false);
if (KeyManager.isPressed("world.heal")) heal.onButtonPress(false);
if (KeyManager.isPressed("world.creative")) gamemode.onButtonPress(false);
}
}
@@ -8,7 +8,6 @@ import org.lwjgl.opengl.GL11;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.api.LayoutStyle;
import codechicken.nei.drawable.DrawableBuilder;
public class LayoutStyleMinecraft extends LayoutStyle {
@@ -31,16 +30,13 @@ public class LayoutStyleMinecraft extends LayoutStyle {
LayoutManager.gamemode.icons[0] = new Image(132, 12, 12, 12);
LayoutManager.gamemode.icons[1] = new Image(156, 12, 12, 12);
LayoutManager.gamemode.icons[2] = new Image(168, 12, 12, 12);
LayoutManager.magnet.icon = new Image(180, 24, 12, 12);
LayoutManager.magnet.icons[0] = new Image(180, 24, 12, 12);
LayoutManager.magnet.icons[1] = new Image(180, 36, 12, 12);
LayoutManager.timeButtons[0].icon = new Image(132, 24, 12, 12);
LayoutManager.timeButtons[1].icon = new Image(120, 24, 12, 12);
LayoutManager.timeButtons[2].icon = new Image(144, 24, 12, 12);
LayoutManager.timeButtons[3].icon = new Image(156, 24, 12, 12);
LayoutManager.heal.icon = new Image(168, 24, 12, 12);
LayoutManager.itemPresenceOverlays[0] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 0, 40, 8, 8)
.build();
LayoutManager.itemPresenceOverlays[1] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 8, 40, 8, 8)
.build();
}
@Override
@@ -86,6 +82,7 @@ public class LayoutStyleMinecraft extends LayoutStyle {
LayoutManager.options.index = NEIClientConfig.getCheatMode();
LayoutManager.magnet.state = 0x4 | (NEIClientConfig.getMagnetMode() ? 1 : 0);
LayoutManager.magnet.index = (LayoutManager.magnet.state & 0x1) != 0 ? 1 : 0;
if (NEIClientConfig.canPerformAction("delete")) {
layoutButton(LayoutManager.delete);
@@ -242,7 +239,11 @@ public class LayoutStyleMinecraft extends LayoutStyle {
Image icon = b.getRenderIcon();
if (icon == null) {
int colour = tex == 2 ? 0xffffa0 : tex == 0 ? 0x601010 : 0xe0e0e0;
int labelColor = b.getRenderLabelColor();
int colour = labelColor != 0 ? labelColor
: tex == 2 ? ColorUtils.buttonLabelHover.getColor()
: tex == 0 ? ColorUtils.buttonLabelDisabled.getColor()
: ColorUtils.buttonLabelNormal.getColor();
drawStringC(b.getRenderLabel(), b.x + b.w / 2, b.y + (b.h - 8) / 2, colour);
} else {
@@ -54,6 +54,7 @@ public class NEIActions {
addAction("magnet", true);
addAction("enchant", true);
addAction("potion", true);
addAction("autocraft");
canDisable.add("dawn");
canDisable.add("noon");
+19 -2
View File
@@ -6,6 +6,7 @@ import static codechicken.nei.PacketIDs.S2C;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.INetHandlerPlayClient;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.world.World;
@@ -14,6 +15,7 @@ import codechicken.core.ClientUtils;
import codechicken.lib.inventory.InventoryUtils;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
import codechicken.nei.commands.CommandBookmarkAdd;
import cpw.mods.fml.relauncher.Side;
public class NEICPH implements IClientPacketHandler {
@@ -56,6 +58,21 @@ public class NEICPH implements IClientPacketHandler {
case S2C.OPEN_POTION_GUI:
ClientUtils.openSMPGui(packet.readUByte(), new GuiPotionCreator(mc.thePlayer.inventory));
break;
case S2C.CACHE_BOOKMARK_DATA:
handleCacheBookmarkData(packet);
break;
case S2C.SYNC:
ServerSync.onReply(packet.readInt());
break;
}
}
private void handleCacheBookmarkData(PacketCustom packet) {
final String payloadId = packet.readString();
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
if (payloadTag != null) {
CommandBookmarkAdd.cachePayload(payloadId, payloadTag);
}
}
@@ -186,9 +203,9 @@ public class NEICPH implements IClientPacketHandler {
packet.sendToServer();
}
public static void sendChatLink(ItemStack stackover) {
public static void sendChatLink(NBTTagCompound payloadTag) {
PacketCustom packet = new PacketCustom(channel, C2S.SEND_CHAT_ITEM_LINK);
packet.writeItemStack(stackover);
packet.writeNBTTagCompound(payloadTag);
packet.sendToServer();
}
+203 -112
View File
@@ -37,7 +37,6 @@ import codechicken.lib.config.ConfigTag;
import codechicken.lib.config.ConfigTagParent;
import codechicken.nei.api.API;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.IConfigureNEI;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.api.NEIInfo;
import codechicken.nei.config.ConfigSet;
@@ -60,8 +59,7 @@ import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.IRecipeHandler;
import codechicken.nei.recipe.InformationHandler;
import codechicken.nei.recipe.RecipeCatalysts;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.util.NEIKeyboardUtils;
import codechicken.nei.util.ItemUntranslator;
import codechicken.obfuscator.ObfuscationRun;
public class NEIClientConfig {
@@ -146,6 +144,9 @@ public class NEIClientConfig {
tag.getTag("inventory.widgetsenabled").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.widgetsenabled"));
tag.getTag("findFuelsParallel").getBooleanValue(true);
tag.getTag("showRepairOnlyForDamageableItems").getBooleanValue(true);
tag.getTag("inventory.autocrafting").getBooleanValue(true);
tag.getTag("inventory.dynamicFontSize").getBooleanValue(true);
tag.getTag("inventory.hidden").getBooleanValue(false);
@@ -198,6 +199,11 @@ public class NEIClientConfig {
tag.getTag("inventory.bookmarks.showRecipeMarkerMode").setComment("Show Recipe Marker").getIntValue(0);
API.addOption(new OptionCycled("inventory.bookmarks.showRecipeMarkerMode", 3, true));
tag.getTag("inventory.bookmarks.maxVerticalTooltipRows")
.setComment("Max rows in vertical crafting chain tooltip lists (0 - disable, always use grid)")
.getIntValue(12);
API.addOption(new OptionIntegerField("inventory.bookmarks.maxVerticalTooltipRows", 0, 50));
tag.getTag("inventory.bookmarks.recipeMarkerColor").setComment("Color of the icon marker")
.getHexValue(0xADADAD);
API.addOption(
@@ -206,8 +212,9 @@ public class NEIClientConfig {
0,
OptionIntegerField.UNSIGNED_INT_MAX));
tag.getTag("inventory.bookmarks.recipeChainDir").getIntValue(1);
API.addOption(new OptionCycled("inventory.bookmarks.recipeChainDir", 2, true));
tag.getTag("inventory.bookmarks.showRecipeHandlerIcon").setComment("Show Recipe Handler Icon")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.bookmarks.showRecipeHandlerIcon", true));
tag.getTag("inventory.bookmarks.ignorePotionOverlap").setComment("Ignore overlap with potion effect HUD")
.getBooleanValue(false);
@@ -217,17 +224,21 @@ public class NEIClientConfig {
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.bookmarks.bookmarkItemsWithRecipe", true));
tag.getTag("inventory.bookmarks.bookmarkRecipeWithCount").setComment("Bookmark recipe with count")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.bookmarks.bookmarkRecipeWithCount", true));
tag.getTag("inventory.bookmarks.craftingTreeZoomWithoutModifier")
.setComment("Zoom Crafting Tree with mouse wheel without holding Ctrl").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.bookmarks.craftingTreeZoomWithoutModifier", true));
tag.getTag("inventory.guirecipe.jeiStyleTabs").setComment("Enable/disable JEI Style Tabs")
.getBooleanValue(true);
API.addOption(new OptionToggleButtonBoubs("inventory.guirecipe.jeiStyleTabs", true));
tag.getTag("inventory.guirecipe.jeiStyleRecipeCatalyst").setComment("Enable/disable JEI Style Recipe Catalysts")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.jeiStyleRecipeCatalyst", true));
tag.getTag("inventory.guirecipe.jeiStyleCycledIngredients").setComment("JEI styled cycled ingredients")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.jeiStyleCycledIngredients", true));
tag.getTag("inventory.guirecipe.jeiStyleRecipeCatalyst")
.setComment("JEI Style Recipe Catalysts (0 - none, 1 - grid, 2 - scroll)").getIntValue(1);
API.addOption(new OptionCycled("inventory.guirecipe.jeiStyleRecipeCatalyst", 3, true));
tag.getTag("inventory.guirecipe.cycledIngredientsTooltip").setComment("Show cycled ingredients tooltip")
.getBooleanValue(true);
@@ -245,13 +256,33 @@ public class NEIClientConfig {
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.slotHighlightPresent", true));
tag.getTag("inventory.guirecipe.infiniteScroll").setComment("Use Infinite Scroll for Handlers")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.guirecipe.infiniteScroll", true));
tag.getTag("inventory.guirecipe.maxHeight").setComment("Recipe GUI Max Height").getIntValue(370);
API.addOption(new OptionIntegerField("inventory.guirecipe.maxHeight", 166, 4000));
tag.getTag("inventory.guirecipe.shiftOverlayRecipe")
.setComment("Require holding shift to move items when overlaying recipe").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.shiftOverlayRecipe", true));
tag.getTag("inventory.guirecipe.scrollPages").setComment("Disable page scroll when overlaying recipe")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.guirecipe.scrollPages", true));
tag.getTag("inventory.guirecipe.freezeCycleOnHover")
.setComment("Stop cycling the recipe while hovering over an ingredient or a button")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.guirecipe.freezeCycleOnHover", true));
tag.getTag("inventory.guirecipe.profile").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true));
tag.getTag("inventory.guirecipe.handlerInfo").setComment("ADVANCED: Handler Info Widget")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.guirecipe.handlerInfo", true));
tag.getTag("inventory.subsets.enabled").setComment("Enable/disable Subsets Dropdown").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.subsets.enabled", true));
@@ -267,6 +298,32 @@ public class NEIClientConfig {
tag.getTag("inventory.history.useRows").setComment("Rows used in historical areas").getIntValue(2);
API.addOption(new OptionIntegerField("inventory.history.useRows", 1, 5));
tag.getTag("inventory.history.worldSpecific").setComment("Global or world specific history")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.history.worldSpecific", true) {
@Override
public boolean onClick(int button) {
super.onClick(button);
ItemPanels.itemPanel.historyPanel.save();
ItemPanels.itemPanel.historyPanel.load();
return true;
}
});
tag.getTag("inventory.history.save").setComment("Enable/disable History save").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.history.save", true) {
@Override
public boolean onClick(int button) {
super.onClick(button);
ItemPanels.itemPanel.historyPanel.save();
ItemPanels.itemPanel.historyPanel.load();
return true;
}
});
tag.getTag("inventory.craftables.enabled").setComment("Enable/disable Craftables Panel").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.craftables.enabled", true));
@@ -277,6 +334,10 @@ public class NEIClientConfig {
tag.getTag("inventory.craftables.useRows").setComment("Rows used in craftables areas").getIntValue(2);
API.addOption(new OptionIntegerField("inventory.craftables.useRows", 1, 5));
tag.getTag("inventory.craftables.favoritesOnly").setComment("Show Only Favorites Recipes")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.craftables.favoritesOnly", true));
tag.getTag("inventory.collapsibleItems.enabled").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.collapsibleItems.enabled", true) {
@@ -285,7 +346,7 @@ public class NEIClientConfig {
super.onClick(button);
CollapsibleItems.saveStates();
CollapsibleItems.load();
LayoutManager.markItemsDirty();
ItemList.refreshItems.restart();
return true;
}
});
@@ -320,6 +381,11 @@ public class NEIClientConfig {
tag.getTag("inventory.itemzoom.onlySolid").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.itemzoom.onlySolid", true));
tag.getTag("inventory.itemzoom.preferredSide")
.setComment("Which side to prefer showing the zoomed item on, when both sides are free.")
.getIntValue(0);
API.addOption(new OptionCycled("inventory.itemzoom.preferredSide", 3, true));
tag.getTag("inventory.itemzoom.helpText")
.setComment("Display name \"Item Zoom\" and the hotkey to toggle this mod below the zoomed item.")
.getBooleanValue(true);
@@ -336,6 +402,13 @@ public class NEIClientConfig {
setFavoriteDefaults(tag);
tag.getTag("inventory.tooltip.showFluidTemperature").setComment("Enabled show Fluid Temperature")
.getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.tooltip.showFluidTemperature", true));
tag.getTag("inventory.tooltip.showFluidState").setComment("Enabled show Fluid State").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.tooltip.showFluidState", true));
tag.getTag("inventory.itemIDs").getIntValue(1);
API.addOption(new OptionCycled("inventory.itemIDs", 3, true));
@@ -424,6 +497,24 @@ public class NEIClientConfig {
tag.getTag("inventory.autocrafting").setComment("Autocrafting").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.autocrafting", true));
tag.getTag("inventory.itemUntranslator").setComment("Item Untranslator").getBooleanValue(true);
API.addOption(new OptionToggleButton("inventory.itemUntranslator", true) {
@Override
public boolean onClick(int button) {
super.onClick(button);
if (enableItemUntranslator()) {
ItemUntranslator.getInstance().load();
} else {
ItemUntranslator.getInstance().unload();
}
return true;
}
});
tag.getTag("tools.handler_load_from_config").setComment("ADVANCED: Load handlers from config")
.getBooleanValue(false);
API.addOption(new OptionToggleButton("tools.handler_load_from_config", true) {
@@ -561,7 +652,7 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
// SearchField.searchParser.clearCache();
SearchField.searchParser.clearCache();
return super.onClick(button);
}
@@ -722,7 +813,7 @@ public class NEIClientConfig {
};
Map<Character, Character> charMap = new Gson().fromJson(prefixRedefinitions, typeToken.getType());
SearchField.searchParser.prefixRedefinitions.putAll(charMap);
SearchField.searchParser.updateRedefinedPrefixes();
SearchField.searchParser.clearCache();
} catch (JsonParseException e) {
logger.warn("Failed to convert prefix redefinitions from JSON to CharToCharMap:", e);
}
@@ -741,8 +832,7 @@ public class NEIClientConfig {
@Override
public boolean onClick(int button) {
super.onClick(button);
FavoriteRecipes.save();
FavoriteRecipes.load();
FavoriteRecipes.reload();
return true;
}
});
@@ -755,39 +845,56 @@ public class NEIClientConfig {
.getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.favorites.showRecipeTooltipInGui", true));
tag.getTag("inventory.favorites.depth").setComment("Bookmark creation depth").getIntValue(3);
tag.getTag("inventory.favorites.generateSingleRecipeFavorites")
.setComment("Automatically generate favorites for items with only one recipe").getBooleanValue(false);
API.addOption(new OptionToggleButton("inventory.favorites.generateSingleRecipeFavorites", true) {
@Override
public boolean onClick(int button) {
super.onClick(button);
FavoriteRecipes.loadAutoGeneratedRecipes();
return true;
}
});
tag.getTag("inventory.favorites.depth").setComment("Bookmark creation depth").getIntValue(9);
API.addOption(new OptionIntegerField("inventory.favorites.depth", 0, 100));
}
private static void setDefaultKeyBindings() {
API.addHashBind("gui.recipe", Keyboard.KEY_R);
API.addHashBind("gui.usage", Keyboard.KEY_U);
API.addKeyBind("gui.back", Keyboard.KEY_BACK);
API.addHashBind("gui.enchant", Keyboard.KEY_X);
API.addHashBind("gui.potion", Keyboard.KEY_P);
API.addKeyBind("gui.enchant", Keyboard.KEY_X);
API.addKeyBind("gui.potion", Keyboard.KEY_P);
API.addKeyBind("gui.prev", Keyboard.KEY_PRIOR);
API.addKeyBind("gui.next", Keyboard.KEY_NEXT);
API.addKeyBind("gui.prev_machine", Keyboard.KEY_UP);
API.addKeyBind("gui.next_machine", Keyboard.KEY_DOWN);
API.addKeyBind("gui.prev_recipe", Keyboard.KEY_LEFT);
API.addKeyBind("gui.next_recipe", Keyboard.KEY_RIGHT);
API.addHashBind("gui.hide", Keyboard.KEY_O);
API.addHashBind("gui.search", Keyboard.KEY_F);
API.addKeyBind("gui.bookmark", Keyboard.KEY_A);
API.addHashBind("gui.favorite", Keyboard.KEY_F + NEIClientUtils.SHIFT_HASH);
API.addHashBind("gui.remove_recipe", Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH);
API.addKeyBind("gui.bookmark_pull_items", Keyboard.KEY_V);
API.addKeyBind("gui.hide", Keyboard.KEY_O);
API.addKeyBind("gui.search", Keyboard.KEY_F);
API.addKeyBind("gui.overlay", Keyboard.KEY_S);
API.addKeyBind("gui.craft_items", Keyboard.KEY_C);
API.addHashBind("gui.hide_bookmarks", Keyboard.KEY_B);
API.addKeyBind("gui.getprevioussearch", Keyboard.KEY_UP);
API.addKeyBind("gui.getnextsearch", Keyboard.KEY_DOWN);
API.addKeyBind("gui.next_tooltip", Keyboard.KEY_Z);
API.addHashBind("gui.itemzoom_toggle", Keyboard.KEY_Z + NEIClientUtils.SHIFT_HASH);
API.addHashBind("gui.itemzoom_hold", 0);
API.addHashBind("gui.itemzoom_zoom_in", 0);
API.addHashBind("gui.itemzoom_zoom_out", 0);
API.addKeyBind("recipe.recipe", Keyboard.KEY_R);
API.addKeyBind("recipe.usage", Keyboard.KEY_U);
API.addKeyBind("recipe.back", Keyboard.KEY_BACK);
API.addKeyBind("recipe.prev_machine", Keyboard.KEY_UP);
API.addKeyBind("recipe.next_machine", Keyboard.KEY_DOWN);
API.addKeyBind("recipe.prev_recipe", Keyboard.KEY_LEFT);
API.addKeyBind("recipe.next_recipe", Keyboard.KEY_RIGHT);
API.addKeyBind("bookmark.add", Keyboard.KEY_A);
API.addKeyBind("bookmark.favorite", Keyboard.KEY_F);
API.addKeyBind("bookmark.favorite_item", Keyboard.KEY_F);
API.addKeyBind("bookmark.remove_recipe", Keyboard.KEY_A);
API.addKeyBind("bookmark.pull_items", Keyboard.KEY_V);
API.addKeyBind("bookmark.chat_link", Keyboard.KEY_L);
API.addKeyBind("bookmark.open_crafting_tree", Keyboard.KEY_T);
API.addKeyBind("bookmark.hide", Keyboard.KEY_B);
API.addKeyBind("itemzoom.toggle", Keyboard.KEY_Z);
API.addKeyBind("itemzoom.hold", 0);
API.addKeyBind("itemzoom.zoom_in", 0);
API.addKeyBind("itemzoom.zoom_out", 0);
API.addKeyBind("world.chunkoverlay", Keyboard.KEY_F9);
API.addKeyBind("world.moboverlay", Keyboard.KEY_F7);
@@ -799,9 +906,10 @@ public class NEIClientConfig {
API.addKeyBind("world.rain", 0);
API.addKeyBind("world.heal", 0);
API.addKeyBind("world.creative", 0);
API.addHashBind("gui.copy_name", Keyboard.KEY_C + NEIClientUtils.CTRL_HASH);
API.addHashBind("gui.copy_oredict", Keyboard.KEY_D + NEIClientUtils.CTRL_HASH);
API.addHashBind("gui.chat_link_item", Keyboard.KEY_L + NEIClientUtils.CTRL_HASH);
API.addKeyBind("copy.name", Keyboard.KEY_C);
API.addKeyBind("copy.oredict", Keyboard.KEY_D);
API.addKeyBind("copy.identifier", Keyboard.KEY_X);
}
public static OptionList getOptionList() {
@@ -809,24 +917,32 @@ public class NEIClientConfig {
}
public static void loadWorld(String worldPath) {
final File specificDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
final boolean newWorld = !specificDir.exists();
unloadWorld();
setInternalEnabled(true);
if (!worldPath.equals(NEIClientConfig.worldPath)) {
if (!worldPath.equals(NEIClientConfig.worldPath) || newWorld) {
NEIClientConfig.worldPath = worldPath;
logger.debug("Loading " + (Minecraft.getMinecraft().isSingleplayer() ? "Local" : "Remote") + " World");
final File specificDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
final boolean newWorld = !specificDir.exists();
if (newWorld) {
specificDir.mkdirs();
}
world = new ConfigSet(new File(specificDir, "NEI.dat"), new ConfigFile(new File(specificDir, "NEI.cfg")));
if (newWorld && Minecraft.getMinecraft().isSingleplayer() && getLockedMode() == -1) {
final int globalMode = global.config.getTag("inventory.cheatmode").getIntValue();
final int worldMode = Math
.min(globalMode, NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0);
if (worldMode != globalMode) {
world.config.getTag("inventory.cheatmode").setIntValue(worldMode);
}
}
bootNEI();
onWorldLoad(newWorld);
onWorldLoad();
}
}
@@ -834,7 +950,7 @@ public class NEIClientConfig {
return NEIClientConfig.worldPath;
}
private static void onWorldLoad(boolean newWorld) {
private static void onWorldLoad() {
world.config.setComment(
"World based configuration of NEI.\nMost of these options can be changed ingame.\nDeleting any element will restore it to it's default value");
@@ -842,12 +958,6 @@ public class NEIClientConfig {
creativeInv = new ItemStack[54];
LayoutManager.searchField.setText(getSearchExpression());
LayoutManager.quantity.setText(Integer.toString(getItemQuantity()));
if (newWorld && Minecraft.getMinecraft().isSingleplayer()) {
world.config.getTag("inventory.cheatmode")
.setIntValue(NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0);
}
NEIInfo.load(ClientUtils.getWorld());
}
@@ -861,63 +971,41 @@ public class NEIClientConfig {
}
public static void unloadWorld() {
if (ItemPanels.bookmarkPanel != null) {
ItemPanels.bookmarkPanel.save();
if (world == null) {
return;
}
ItemPanels.bookmarkPanel.save();
ItemPanels.itemPanel.historyPanel.save();
SubsetWidget.saveHidden();
FavoriteRecipes.save();
CollapsibleItems.saveStates();
if (world != null) {
world.saveNBT();
}
world.saveNBT();
}
private static final Map<String, String> keySettings = new HashMap<>();
public static int getKeyBinding(String string) {
final String key = keySettings.computeIfAbsent(string, s -> "keys." + s);
return getSetting(key).getIntValue(Keyboard.KEY_NONE);
return KeyManager.getKeyCode(string);
}
public static void setDefaultKeyBinding(String string, int key) {
getSetting("keys." + string).getIntValue(key);
KeyManager.registerKeyBinding(string, key);
}
public static boolean isKeyHashDown(String string) {
final int hash = getKeyBinding(string);
if (hash != Keyboard.CHAR_NONE && Keyboard.getEventKeyState()) {
return KeyManager.keyStates.containsKey(string) ? Keyboard.isKeyDown(NEIKeyboardUtils.unhash(hash))
: hash == NEIClientUtils.getKeyHash();
}
return false;
return KeyManager.isHashDown(string);
}
public static String getKeyName(String keyBind) {
return getKeyName(keyBind, 0);
return KeyManager.getKeyName(keyBind);
}
public static String getKeyName(String keyBind, int meta, int mouseBind) {
final int hash = getKeyBinding(keyBind);
if (hash == Keyboard.CHAR_NONE) {
return null;
}
return NEIClientUtils.getKeyName(hash + meta, mouseBind);
return KeyManager.getKeyName(keyBind, meta, mouseBind);
}
public static String getKeyName(String keyBind, int meta) {
final int hash = getKeyBinding(keyBind);
if (hash == Keyboard.CHAR_NONE) {
return null;
}
return NEIKeyboardUtils.getKeyName(hash + meta);
return KeyManager.getKeyName(keyBind, meta);
}
public static void bootNEI() {
@@ -926,7 +1014,6 @@ public class NEIClientConfig {
// main NEI config loading
ItemInfo.load();
GuiInfo.load();
RecipeInfo.load();
HeldItemHandler.load();
LayoutManager.load();
NEIController.load();
@@ -942,23 +1029,14 @@ public class NEIClientConfig {
? NEIClientConfig.pluginsList.parallelStream()
: NEIClientConfig.pluginsList.stream();
stream.forEach(clazz -> {
try {
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
config.loadConfig();
NEIModContainer.plugins.add(config);
logger.debug("Loaded {}", clazz.getName());
} catch (Throwable e) {
logger.error("Failed to Load {}", clazz.getName(), e);
}
});
PluginLoader.load(stream, NEIModContainer.plugins, logger);
KeyManager.requestOptionsReload();
RecipeCatalysts.loadCatalystInfo();
SubsetWidget.loadCustomSubsets();
SubsetWidget.loadHidden();
CollapsibleItems.load();
ItemSorter.loadConfig();
FavoriteRecipes.load();
// Set pluginNEIConfigLoaded here before posting the NEIConfigsLoadedEvent. This used to be the
// other way around, but apparently if your modpack includes 800 mods the event poster might not
@@ -967,13 +1045,16 @@ public class NEIClientConfig {
// API. I don't expect any handler to do this, but who knows what modders have come up with...
pluginNEIConfigLoaded = true;
MinecraftForge.EVENT_BUS.post(new NEIConfigsLoadedEvent());
ItemList.loadItems.restart();
}
}.start();
} else {
SubsetWidget.loadHidden();
ItemList.loadItems.restart();
if (ItemList.loadFinished) {
ItemList.refreshItems.restart();
} else {
ItemList.loadItems.restart();
}
}
}
@@ -997,7 +1078,9 @@ public class NEIClientConfig {
}
public static boolean autocraftingEnabled() {
return getBooleanSetting("inventory.autocrafting");
if (!getBooleanSetting("inventory.autocrafting")) return false;
if (hasSMPCounterpart) return permissableActions.contains("autocraft");
return true;
}
public static boolean favoritesEnabled() {
@@ -1028,6 +1111,10 @@ public class NEIClientConfig {
return getIntSetting("inventory.bookmarks.showRecipeMarkerMode");
}
public static int getMaxVerticalTooltipRows() {
return getIntSetting("inventory.bookmarks.maxVerticalTooltipRows");
}
public static boolean showItemQuantityWidget() {
return getBooleanSetting("inventory.showItemQuantityWidget");
}
@@ -1064,8 +1151,8 @@ public class NEIClientConfig {
return getBooleanSetting("inventory.guirecipe.slotHighlightPresent");
}
public static boolean areJEIStyleRecipeCatalystsVisible() {
return getBooleanSetting("inventory.guirecipe.jeiStyleRecipeCatalyst");
public static int getJEIStyleRecipeCatalysts() {
return getIntSetting("inventory.guirecipe.jeiStyleRecipeCatalyst");
}
public static boolean useCreativeTabStyle() {
@@ -1076,22 +1163,22 @@ public class NEIClientConfig {
return getBooleanSetting("inventory.bookmarks.ignorePotionOverlap");
}
public static int recipeChainDir() {
return getIntSetting("inventory.bookmarks.recipeChainDir");
public static boolean craftingTreeZoomWithoutModifier() {
return getBooleanSetting("inventory.bookmarks.craftingTreeZoomWithoutModifier");
}
public static boolean optimizeGuiOverlapComputation() {
return getBooleanSetting("inventory.optimize_gui_overlap_computation");
}
public static boolean useJEIStyledCycledIngredients() {
return getBooleanSetting("inventory.guirecipe.jeiStyleCycledIngredients");
}
public static boolean showCycledIngredientsTooltip() {
return getBooleanSetting("inventory.guirecipe.cycledIngredientsTooltip");
}
public static boolean freezeCycleOnHover() {
return getBooleanSetting("inventory.guirecipe.freezeCycleOnHover");
}
public static boolean requireShiftForOverlayRecipe() {
return getBooleanSetting("inventory.guirecipe.shiftOverlayRecipe");
}
@@ -1208,6 +1295,10 @@ public class NEIClientConfig {
return getBooleanSetting("inventory.collapsibleItems.enabled");
}
public static boolean enableItemUntranslator() {
return getBooleanSetting("inventory.itemUntranslator");
}
public static boolean getMagnetMode() {
return enabledActions.contains("magnet");
}
+123 -72
View File
@@ -11,7 +11,6 @@ import static codechicken.nei.NEIClientConfig.world;
import java.awt.Color;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
@@ -21,12 +20,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import net.minecraft.client.Minecraft;
@@ -40,13 +39,16 @@ import net.minecraft.inventory.Container;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fluids.FluidStack;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import com.google.common.collect.Iterables;
import com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatUtil;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.inventory.InventoryRange;
@@ -56,8 +58,9 @@ import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.IInfiniteItemHandler;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.recipe.GuiOverlayButton.ItemOverlayState;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NEIKeyboardUtils;
import codechicken.nei.util.NEIMouseUtils;
public class NEIClientUtils extends NEIServerUtils {
@@ -95,6 +98,7 @@ public class NEIClientUtils extends NEIServerUtils {
/** Formats a number with group separator and at most 2 fraction digits. */
private static final Map<Locale, DecimalFormat> decimalFormatters = new HashMap<>();
private static final DecimalFormat CHANCE_FORMAT = new DecimalFormat("##0.##%");
public static final int ALT_HASH = NEIKeyboardUtils.ALT_HASH;
public static final int SHIFT_HASH = NEIKeyboardUtils.SHIFT_HASH;
@@ -126,16 +130,26 @@ public class NEIClientUtils extends NEIServerUtils {
});
}
public static String formatNumbers(BigInteger aNumber) {
public static String formatNumber(Number aNumber) {
if (NEIModContainer.isGTNHLibLoaded()) {
return NumberFormatUtil.formatNumber(aNumber);
}
return getDecimalFormat().format(aNumber);
}
public static String formatNumbers(long aNumber) {
return getDecimalFormat().format(aNumber);
public static String formatFluid(Number aNumber) {
if (NEIModContainer.isGTNHLibLoaded()) {
return NumberFormatUtil.formatFluid(aNumber);
}
return getDecimalFormat().format(aNumber) + " L";
}
public static String formatNumbers(double aNumber) {
return getDecimalFormat().format(aNumber);
public static String formatChance(float chance) {
return CHANCE_FORMAT.format(chance);
}
public static void deleteHeldItem() {
@@ -198,7 +212,7 @@ public class NEIClientUtils extends NEIServerUtils {
public static void cheatItem(ItemStack stack, int button, int mode) {
if (!canCheatItem(stack)) return;
if (mode == -1 && button == 0 && shiftKey() && NEIClientConfig.hasSMPCounterPart()) {
if (mode == -1 && button == 0 && controlKey() && NEIClientConfig.hasSMPCounterPart()) {
for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers) {
if (!handler.canHandleItem(stack)) continue;
@@ -283,6 +297,83 @@ public class NEIClientUtils extends NEIServerUtils {
return InventoryUtils.getInsertibleQuantity(new InventoryRange(player.inventory, 0, 36), itemstack) > 0;
}
public static List<ItemOverlayState> presenceOverlay(List<PositionedStack> ingredients, List<ItemStack> invStacks) {
final List<ItemOverlayState> states = new ArrayList<>();
final Map<ItemStack, Long> remainingFluid = new IdentityHashMap<>();
for (PositionedStack stack : ingredients) {
boolean found;
if (StackInfo.isFluidDisplayItem(stack.item)) {
final FluidStack requiredFluid = StackInfo.getFluid(stack.item);
found = requiredFluid != null && consumeFluid(invStacks, remainingFluid, requiredFluid);
} else {
found = false;
for (ItemStack is : invStacks) {
if (is.stackSize > 0 && stack.contains(is)) {
is.stackSize--;
found = true;
break;
}
}
}
states.add(new ItemOverlayState(stack, found));
}
return states;
}
private static boolean consumeFluid(List<ItemStack> invStacks, Map<ItemStack, Long> remainingFluid,
FluidStack requiredFluid) {
long need = requiredFluid.amount;
final List<ItemStack> matched = new ArrayList<>();
for (ItemStack is : invStacks) {
if (need <= 0) {
break;
}
if (is.stackSize <= 0) {
continue;
}
final FluidStack invFluid = StackInfo.getFluid(is);
if (invFluid == null || !invFluid.isFluidEqual(requiredFluid)) {
continue;
}
final long available = remainingFluid.computeIfAbsent(is, k -> (long) invFluid.amount * is.stackSize);
if (available <= 0) {
continue;
}
matched.add(is);
need -= available;
}
if (need > 0) {
return false;
}
long remainingNeed = requiredFluid.amount;
for (ItemStack is : matched) {
final long available = remainingFluid.get(is);
final long consumed = Math.min(available, remainingNeed);
remainingFluid.put(is, available - consumed);
remainingNeed -= consumed;
if (remainingNeed <= 0) {
break;
}
}
return true;
}
public static boolean shiftKey() {
return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
}
@@ -370,8 +461,8 @@ public class NEIClientUtils extends NEIServerUtils {
else sendCommand(getStringSetting("command.heal"), mc().thePlayer.getCommandSenderName());
}
public static void sendChatItemLink(ItemStack stackover) {
if (hasSMPCounterPart()) NEICPH.sendChatLink(stackover);
public static void sendChatItemLink(NBTTagCompound payload) {
if (hasSMPCounterPart()) NEICPH.sendChatLink(payload);
}
public static void toggleMagnetMode() {
@@ -413,18 +504,11 @@ public class NEIClientUtils extends NEIServerUtils {
if (textWidth > containerWidth) {
int dots = fontRenderer.getStringWidth("...");
if (containerWidth > dots) {
textWidth += dots;
while (textWidth > containerWidth) {
textWidth -= fontRenderer.getCharWidth(text.charAt(text.length() - 1));
text = text.substring(0, text.length() - 1);
}
return text + "...";
if (containerWidth <= dots) {
return "...";
}
return "...";
return fontRenderer.trimStringToWidth(text, containerWidth - dots) + "...";
}
return text;
@@ -452,55 +536,8 @@ public class NEIClientUtils extends NEIServerUtils {
return Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU);
}
public static int getMetaHash() {
if (Keyboard.getEventKeyState()) {
int hash = 0;
if (altKey()) {
hash = hash | ALT_HASH;
}
if (shiftKey()) {
hash = hash | SHIFT_HASH;
}
if (controlKey()) {
hash = hash | CTRL_HASH;
}
return hash;
}
return 0;
}
public static int getKeyHash() {
if (Keyboard.getEventKeyState()) {
final int keycode = Keyboard.getEventKey();
if (!NEIKeyboardUtils.isHashKey(keycode)) {
return getMetaHash() + keycode;
}
}
return Keyboard.CHAR_NONE;
}
public static String getKeyName(int keyBind, int mouseBind) {
StringJoiner keyText = new StringJoiner(" + ");
String keyHash = keyBind == Keyboard.KEY_NONE ? "" : NEIKeyboardUtils.getKeyName(keyBind);
String mouseHash = mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? "" : NEIMouseUtils.getKeyName(mouseBind);
if (!keyHash.isEmpty()) {
keyText.add(keyHash);
}
if (!mouseHash.isEmpty()) {
keyText.add(mouseHash);
}
return keyText.toString();
return KeyManager.getKeyName(keyBind, mouseBind);
}
public static void playClickSound() {
@@ -527,8 +564,20 @@ public class NEIClientUtils extends NEIServerUtils {
}
}
public static long getScrollMultiplier(ItemStack stack) {
final long multiplier = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
if (multiplier != 0) {
return multiplier;
}
return StackInfo.isFluidDisplayItem(stack) ? 144 : stack.getMaxStackSize();
}
public static void drawRect(double left, double top, double width, double height, Color color) {
Tessellator tessellator = Tessellator.instance;
final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D);
final boolean isBlend = GL11.glGetBoolean(GL11.GL_BLEND);
final Tessellator tessellator = Tessellator.instance;
GL11.glEnable(GL11.GL_BLEND);
GL11.glDisable(GL11.GL_TEXTURE_2D);
@@ -540,8 +589,9 @@ public class NEIClientUtils extends NEIServerUtils {
tessellator.addVertex(left + width, top, 0.0D);
tessellator.addVertex(left, top, 0.0D);
tessellator.draw();
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glDisable(GL11.GL_BLEND);
GL11.glColor4f(1, 1, 1, 1);
if (is2DTexture) GL11.glEnable(GL11.GL_TEXTURE_2D);
if (!isBlend) GL11.glDisable(GL11.GL_BLEND);
}
public static void drawNEIOverlayText(String text, Rectangle4i rect, float scale, int color, boolean shadow,
@@ -566,6 +616,7 @@ public class NEIClientUtils extends NEIServerUtils {
GuiDraw.fontRenderer.drawString(text, 0, 0, color, shadow);
GL11.glScaled(1 / smallTextScale, 1 / smallTextScale, 1);
GL11.glTranslated(-1 * offsetX, -1 * offsetY, 0);
GL11.glColor4f(1, 1, 1, 1);
});
}
@@ -1,19 +1,21 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.getMousePosition;
import static codechicken.nei.NEIClientConfig.canCheatItem;
import java.awt.Point;
import java.util.LinkedList;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.gui.inventory.GuiContainerCreative;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Slot;
import net.minecraft.inventory.SlotCrafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.client.ClientCommandHandler;
import codechicken.core.CommonUtils;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.api.API;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.IInfiniteItemHandler;
@@ -21,10 +23,11 @@ import codechicken.nei.api.INEIGuiHandler;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.commands.CommandBookmarkAdd;
import codechicken.nei.commands.CommandRecipeId;
import codechicken.nei.commands.CommandUntranslator;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.guihook.IContainerSlotClickHandler;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.util.EmptyContainer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -68,34 +71,44 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
public static void registerClientCommands() {
ClientCommandHandler.instance.registerCommand(new CommandBookmarkAdd());
ClientCommandHandler.instance.registerCommand(new CommandRecipeId());
ClientCommandHandler.instance.registerCommand(new CommandUntranslator());
}
public static void updateUnlimitedItems(InventoryPlayer inventory) {
if (!NEIClientConfig.canPerformAction("item") || !NEIClientConfig.hasSMPCounterPart()) return;
LinkedList<ItemStack> beforeStacks = new LinkedList<>();
for (int i = 0; i < inventory.getSizeInventory(); i++)
beforeStacks.add(NEIServerUtils.copyStack(inventory.getStackInSlot(i)));
for (int i = 0; i < inventory.getSizeInventory(); i++) {
int size = inventory.getSizeInventory();
for (int i = 0; i < size; i++) {
ItemStack stack = inventory.getStackInSlot(i);
if (stack == null) continue;
for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers)
if (handler.canHandleItem(stack) && handler.isItemInfinite(stack))
handler.replenishInfiniteStack(inventory, i);
}
for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers) {
if (!handler.canHandleItem(stack) || !handler.isItemInfinite(stack)) continue;
for (int i = 0; i < inventory.getSizeInventory(); i++) {
ItemStack newstack = inventory.getStackInSlot(i);
long beforeSize = packStackSize(stack.stackSize, stack.getItemDamage());
handler.replenishInfiniteStack(inventory, i);
if (!NEIServerUtils.areStacksIdentical(beforeStacks.get(i), newstack)) {
inventory.setInventorySlotContents(i, beforeStacks.get(i)); // restore in case of SMP fail
NEIClientUtils.setSlotContents(i, newstack, false); // sends via SMP handler ;)
ItemStack newstack = inventory.getStackInSlot(i);
if (!sameStack(stack, beforeSize, newstack)) {
NEIClientUtils.setSlotContents(i, newstack, false); // sends via SMP handler ;)
stack.stackSize = (int) (beforeSize >> 32);
stack.setItemDamage((int) beforeSize);
inventory.setInventorySlotContents(i, stack); // restore in case of SMP fail
}
}
}
}
private static long packStackSize(int stackSize, int itemDamage) {
return ((long) stackSize << 32) | (itemDamage & 0xFFFFFFFFL);
}
private static boolean sameStack(ItemStack before, long beforeSize, ItemStack stack) {
if (stack == null) return before == null;
return before != null && Item.getIdFromItem(before.getItem()) == Item.getIdFromItem(stack.getItem())
&& beforeSize == packStackSize(stack.stackSize, stack.getItemDamage());
}
public static void processCreativeCycling(InventoryPlayer inventory) {
if (NEIClientConfig.invCreativeMode() && NEIClientUtils.controlKey()) {
if (selectedItem != inventory.currentItem) {
@@ -136,7 +149,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
return true;
}
if (button == 1 && slot instanceof SlotCrafting) // right click
if (button == 1 && modifier != 2 && slot instanceof SlotCrafting) // right click
{
for (int i1 = 0; i1 < 64; i1++) // click this slot 64 times
manager.handleSlotClick(slot.slotNumber, button, 0);
@@ -148,26 +161,40 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
&& slot != null
&& slot.getStack() != null
&& slot.isItemValid(slot.getStack())) {
if (!canCheatItem(slot.getStack())) return false;
NEIClientUtils.cheatItem(slot.getStack(), button, 1);
return true;
}
if (GuiInfo.hasCustomSlots(gui)) return false;
if (slotIndex >= 0 && NEIClientUtils.shiftKey()
&& NEIClientUtils.getHeldItem() != null
&& !slot.getHasStack()) {
ItemStack held = NEIClientUtils.getHeldItem();
manager.handleSlotClick(slot.slotNumber, button, 0);
if (slot.isItemValid(held) && !ItemInfo.fastTransferExemptions.contains(slot.getClass()))
fastTransferManager.performMassTransfer(gui, pickedUpFromSlot, slotIndex, held);
if (NEIClientUtils.shiftKey() && button != 2) { // MMB is used by other mods when an itemstack is picked.
// fix small mouse movement while clicking causing slotIndex to be -999
if (slotIndex == -999) {
final Point mouse = GuiDraw.getMousePosition();
slot = gui.getSlotAtPosition(mouse.x, mouse.y);
return true;
}
if (slot != null) {
slotIndex = slot.slotNumber;
}
}
if (slotIndex == -999 && NEIClientUtils.shiftKey() && button == 0) {
fastTransferManager.throwAll(gui, pickedUpFromSlot);
return true;
if (slotIndex >= 0 && NEIClientUtils.getHeldItem() != null && !slot.getHasStack()) {
final ItemStack held = NEIClientUtils.getHeldItem();
manager.handleSlotClick(slot.slotNumber, button, 0);
if (slot.isItemValid(held) && !ItemInfo.fastTransferExemptions.contains(slot.getClass())) {
fastTransferManager.performMassTransfer(gui, pickedUpFromSlot, slotIndex, held);
}
return true;
}
if (slotIndex == -999 && button == 0) {
fastTransferManager.throwAll(gui, pickedUpFromSlot);
return true;
}
}
return false;
@@ -234,7 +261,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
// slot in GuiRecipe does not contain actual stack
// and scroll should be handled by GuiRecipe, not here
if (gui instanceof GuiRecipe) return false;
if (gui.inventorySlots instanceof EmptyContainer) return false;
Point mousePos = getMousePosition();
Slot mouseover = manager.window.getSlotAtPosition(mousePos.x, mousePos.y);
@@ -7,8 +7,7 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import net.minecraft.util.EnumChatFormatting;
import java.util.concurrent.CopyOnWriteArrayList;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
@@ -18,6 +17,7 @@ import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.nei.api.IConfigureNEI;
import codechicken.nei.asm.NEICorePlugin;
import codechicken.nei.config.IMCHandler;
import codechicken.nei.guihook.HideousLinkedList;
import codechicken.nei.recipe.GuiRecipeTab;
import cpw.mods.fml.client.FMLFileResourcePack;
import cpw.mods.fml.client.FMLFolderResourcePack;
@@ -31,22 +31,24 @@ import cpw.mods.fml.common.event.FMLInterModComms;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerAboutToStartEvent;
import cpw.mods.fml.common.versioning.ArtifactVersion;
import cpw.mods.fml.common.versioning.VersionParser;
import cpw.mods.fml.common.versioning.VersionRange;
@SuppressWarnings("UnstableApiUsage")
public class NEIModContainer extends DummyModContainer {
public static LinkedList<IConfigureNEI> plugins = new LinkedList<>();
public static final LinkedList<IConfigureNEI> plugins = new HideousLinkedList<>(new CopyOnWriteArrayList<>());
private static boolean gregTech5Loaded;
private static boolean gtnhLibLoaded;
private static boolean cubicChunksLoaded;
private static ASMDataTable asmDataTable;
public NEIModContainer() {
super(getModMetadata());
loadMetadata();
}
private static ModMetadata getModMetadata() {
@@ -54,7 +56,7 @@ public class NEIModContainer extends DummyModContainer {
modMetadata.name = "NotEnoughItems";
modMetadata.modId = "NotEnoughItems";
modMetadata.version = Tags.VERSION;
modMetadata.authorList = Arrays.asList("ChickenBones", "mitchej123");
modMetadata.authorList = Arrays.asList("ChickenBones", "mitchej123", "SLPrime");
modMetadata.url = "https://github.com/GTNewHorizons/NotEnoughItems";
modMetadata.description = "Recipe Viewer, Inventory Manager, Item Spawner, Cheats and more; GTNH Version includes many enhancements.";
return modMetadata;
@@ -68,6 +70,10 @@ public class NEIModContainer extends DummyModContainer {
return gtnhLibLoaded;
}
public static boolean isCCLoaded() {
return cubicChunksLoaded;
}
@Override
public Set<ArtifactVersion> getRequirements() {
Set<ArtifactVersion> deps = new HashSet<>();
@@ -79,36 +85,12 @@ public class NEIModContainer extends DummyModContainer {
public List<ArtifactVersion> getDependencies() {
List<ArtifactVersion> deps = new ArrayList<>();
deps.add(VersionParser.parseVersionReference("CodeChickenCore@[" + codechicken.core.asm.Tags.VERSION + ",)"));
deps.add(VersionParser.parseVersionReference("gtnhlib@[0.6.0,)"));
deps.add(VersionParser.parseVersionReference("gtnhlib@[0.9.6,)"));
return deps;
}
private String description;
private void loadMetadata() {
description = super.getMetadata().description.replace("Supporters:", EnumChatFormatting.AQUA + "Supporters:");
}
@Override
public ModMetadata getMetadata() {
StringBuilder s_plugins = new StringBuilder();
if (plugins.size() == 0) {
s_plugins.append(EnumChatFormatting.RED).append("No installed plugins.");
} else {
s_plugins.append(EnumChatFormatting.GREEN).append("Installed plugins: ");
for (int i = 0; i < plugins.size(); i++) {
if (i > 0) s_plugins.append(", ");
IConfigureNEI plugin = plugins.get(i);
s_plugins.append(plugin.getName()).append(" ").append(plugin.getVersion());
}
s_plugins.append(".");
}
ModMetadata meta = super.getMetadata();
meta.description = description.replace("<plugins>", s_plugins.toString());
return meta;
}
@Override
public boolean registerBus(EventBus bus, LoadController controller) {
bus.register(this);
@@ -119,14 +101,18 @@ public class NEIModContainer extends DummyModContainer {
public void preInit(FMLPreInitializationEvent event) {
gregTech5Loaded = Loader.isModLoaded("gregtech") && !Loader.isModLoaded("gregapi_post");
gtnhLibLoaded = Loader.isModLoaded("gtnhlib");
cubicChunksLoaded = Loader.isModLoaded("cubicchunks");
ServerHandler.preInit();
if (CommonUtils.isClient()) ClientHandler.preInit();
asmDataTable = event.getAsmData();
}
@Subscribe
public void init(FMLInitializationEvent event) {
if (CommonUtils.isClient()) ClientHandler.load();
if (CommonUtils.isClient()) {
ClientHandler.load();
IMCForNEI.IMCSender();
}
ServerHandler.load();
}
@@ -147,6 +133,11 @@ public class NEIModContainer extends DummyModContainer {
}
}
@Subscribe
public void onServerAboutToStart(FMLServerAboutToStartEvent event) {
NEIServerConfig.resetFirstLoad();
}
@Subscribe
public void handleIMCMessages(FMLInterModComms.IMCEvent event) {
IMCHandler.processIMC(event.getMessages());
@@ -1,14 +1,10 @@
package codechicken.nei;
import java.util.Collection;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.InventoryEffectRenderer;
import net.minecraft.potion.PotionEffect;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.api.INEIGuiAdapter;
/**
@@ -18,35 +14,37 @@ public class NEIPotionGuiHandler extends INEIGuiAdapter {
@Override
public boolean hideItemPanelSlot(GuiContainer guiContainer, int slotX, int slotY, int slotW, int slotH) {
if (NEIClientConfig.ignorePotionOverlap()) {
if (!(guiContainer instanceof InventoryEffectRenderer)) {
return false;
}
if (guiContainer instanceof InventoryEffectRenderer) {
int x = guiContainer.guiLeft - 124;
int y = guiContainer.guiTop;
Minecraft minecraft = guiContainer.mc;
if (minecraft == null) {
return false;
}
EntityPlayerSP player = minecraft.thePlayer;
if (player == null) {
return false;
}
Collection<PotionEffect> activePotionEffects = player.getActivePotionEffects();
if (activePotionEffects.isEmpty()) {
return false;
}
int height = 33;
if (activePotionEffects.size() > 5) {
height = 132 / (activePotionEffects.size() - 1);
}
Rectangle4i slotRect = new Rectangle4i(slotX, slotY, slotW, slotH);
for (PotionEffect potioneffect : activePotionEffects) {
Rectangle4i box = new Rectangle4i(x, y, 140, 32);
if (box.intersects(slotRect)) return true;
y += height;
}
int x = guiContainer.guiLeft - 124;
if (slotX + slotW <= x || slotX >= x + 140) {
return false;
}
Minecraft minecraft = guiContainer.mc;
if (minecraft == null) {
return false;
}
EntityPlayerSP player = minecraft.thePlayer;
if (player == null) {
return false;
}
int potionCount = player.getActivePotionEffects().size();
if (potionCount == 0 || NEIClientConfig.ignorePotionOverlap()) {
return false;
}
int height = 33;
if (potionCount > 5) {
height = 132 / (potionCount - 1);
}
return intersectsPotionEffects(slotY, slotH, guiContainer.guiTop, potionCount, height);
}
static boolean intersectsPotionEffects(int slotY, int slotH, int effectY, int potionCount, int height) {
for (int i = 0; i < potionCount; i++, effectY += height) {
if (slotY + slotH > effectY && slotY < effectY + 32) return true;
}
return false;
}
+11 -1
View File
@@ -11,6 +11,7 @@ import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.InventoryBasic;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.INetHandlerPlayServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityMobSpawner;
@@ -91,11 +92,20 @@ public class NEISPH implements IServerPacketHandler {
case C2S.SEND_CHAT_ITEM_LINK:
handleSendChatItemLink(sender, packet);
break;
case C2S.SYNC:
handleSync(sender, packet.readInt());
break;
}
}
private void handleSync(EntityPlayerMP sender, int id) {
sender.sendContainerAndContentsToPlayer(sender.openContainer, sender.openContainer.getInventory());
new PacketCustom(channel, S2C.SYNC).writeInt(id).sendToPlayer(sender);
}
private void handleSendChatItemLink(EntityPlayerMP sender, PacketCustom packet) {
NEIServerUtils.sendChatItemLink(sender, packet.readItemStack());
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
NEIServerUtils.sendChatItemLink(sender, payloadTag);
}
private void handleDummySlotSet(EntityPlayerMP sender, PacketCustom packet) {
@@ -18,7 +18,6 @@ import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.JsonToNBT;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.permission.PermissionLevel;
@@ -37,21 +36,23 @@ import codechicken.lib.packet.PacketCustom;
public class NEIServerConfig {
private static MinecraftServer server;
private static boolean firstLoad;
public static Logger logger = LogManager.getLogger("NotEnoughItems");
public static File saveDir;
public static ConfigFile serverConfig;
public static Map<Integer, NBTTagCompound> dimTags = new HashMap<>();
public static HashMap<String, PlayerSave> playerSaves = new HashMap<>();
public static ItemStackMap<Set<String>> bannedItems = new ItemStackMap<>();
private static boolean doesPermAPIExist = false;
public static void resetFirstLoad() {
firstLoad = true;
}
public static void load(World world) {
if (MinecraftServer.getServer() != server) {
if (firstLoad) {
firstLoad = false;
logger.debug("Loading NEI Server");
server = MinecraftServer.getServer();
saveDir = new File(DimensionManager.getCurrentSaveRootDirectory(), "NEI");
dimTags.clear();
@@ -107,6 +108,7 @@ public class NEIServerConfig {
setDefaultFeature("save-state");
setDefaultFeature("item");
setDefaultFeature("delete");
setDefaultFeature("autocraft", "ALL");
setDefaultFeature("notify-item", "CONSOLE, OP");
}
+102 -31
View File
@@ -10,6 +10,7 @@ import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.zip.ZipException;
import net.minecraft.command.ICommandSender;
@@ -28,12 +29,13 @@ import net.minecraft.nbt.NBTException;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.WorldSettings.GameType;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.oredict.OreDictionary;
import org.apache.logging.log4j.Level;
@@ -44,6 +46,8 @@ import codechicken.lib.inventory.InventoryRange;
import codechicken.lib.inventory.InventoryUtils;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.PacketIDs.S2C;
import codechicken.nei.item.ItemFluidDisplay;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTHelper;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.registry.GameRegistry;
@@ -51,15 +55,18 @@ import cpw.mods.fml.relauncher.ReflectionHelper;
public class NEIServerUtils {
private static Class<?> gtMetaBaseItem;
private static class GTMetaBaseItemHolder {
static {
try {
final ClassLoader loader = NEIServerUtils.class.getClassLoader();
NEIServerUtils.gtMetaBaseItem = ReflectionHelper
.getClass(loader, "gregtech.api.items.MetaBaseItem", "gregtech.api.items.GT_MetaBase_Item");
} catch (Exception ignored) {
/* Do nothing */
private static final Class<?> gtMetaBaseItem = loadMetaBaseItem();
private static Class<?> loadMetaBaseItem() {
try {
final ClassLoader loader = NEIServerUtils.class.getClassLoader();
return ReflectionHelper
.getClass(loader, "gregtech.api.items.MetaBaseItem", "gregtech.api.items.GT_MetaBase_Item");
} catch (Exception ignored) {
return null;
}
}
}
@@ -70,7 +77,7 @@ public class NEIServerUtils {
public static void toggleRaining(World world, boolean notify) {
boolean raining = !world.isRaining();
if (!raining) // turn off
((WorldServer) world).provider.resetRainAndThunder();
world.provider.resetRainAndThunder();
else world.getWorldInfo().setRaining(!isRaining(world));
if (notify)
@@ -83,16 +90,46 @@ public class NEIServerUtils {
player.extinguish();
}
public static void sendChatItemLink(EntityPlayerMP sender, ItemStack stackover) {
ServerUtils.sendChatToAll(
new ChatComponentTranslation(
"nei.chat.item_link.text",
sender.getDisplayName(),
stackover.func_151000_E().setChatStyle(
stackover.func_151000_E().getChatStyle().setChatClickEvent(
new ClickEvent(
ClickEvent.Action.RUN_COMMAND,
"/nei_bookmark " + stackover.writeToNBT(new NBTTagCompound()))))));
public static void sendChatItemLink(EntityPlayerMP sender, NBTTagCompound payload) {
if (payload == null) {
sender.addChatMessage(new ChatComponentText("Invalid bookmark payload"));
return;
}
final String linkType = payload.getString("linkType");
final String payloadId = UUID.randomUUID().toString().substring(0, 8);
final PacketCustom packet = new PacketCustom(NEISPH.channel, S2C.CACHE_BOOKMARK_DATA).writeString(payloadId)
.writeNBTTagCompound((NBTTagCompound) payload.copy());
for (EntityPlayer player : ServerUtils.getPlayers()) {
if (player instanceof EntityPlayerMP playerMP) {
packet.sendToPlayer(playerMP);
}
}
try {
final IChatComponent clickable;
if (payload.hasKey("item")) {
clickable = StackInfo.loadFromNBT(payload.getCompoundTag("item")).func_151000_E();
} else {
clickable = new ChatComponentText("[")
.appendSibling(new ChatComponentTranslation("nei.chat.item_link." + linkType + ".text"))
.appendText("]");
}
if (clickable == null) {
throw new Exception("Invalid ItemStack NBT");
}
clickable.getChatStyle()
.setChatClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/nei_bookmark " + payloadId));
ServerUtils.sendChatToAll(
new ChatComponentTranslation("nei.chat.item_link.text", sender.getDisplayName(), clickable));
} catch (Exception e) {
sender.addChatMessage(new ChatComponentText(e.toString()));
}
}
public static long getTime(World world) {
@@ -180,7 +217,7 @@ public class NEIServerUtils {
public static boolean areStacksSameTypeWithNBT(ItemStack stack1, ItemStack stack2) {
return stack1 != null && stack2 != null
&& stack1.getItem() == stack2.getItem()
&& (!stack2.getHasSubtypes() || stack2.getItemDamage() == stack1.getItemDamage())
&& (stack2.isItemStackDamageable() || stack2.getItemDamage() == stack1.getItemDamage())
&& NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound());
}
@@ -226,12 +263,10 @@ public class NEIServerUtils {
/**
* GT Items don't have any NBT set for the recipe, so if either of the stacks has a NULL nbt, and the other doesn't,
* pretend they stack
*
* @param ItemStack stack
* @return
*/
public static boolean isItemTool(ItemStack stack) {
return NEIServerUtils.gtMetaBaseItem != null && NEIServerUtils.gtMetaBaseItem.isInstance(stack.getItem());
return GTMetaBaseItemHolder.gtMetaBaseItem != null
&& GTMetaBaseItemHolder.gtMetaBaseItem.isInstance(stack.getItem());
}
/**
@@ -409,17 +444,53 @@ public class NEIServerUtils {
throw new RuntimeException(msg);
}
@SuppressWarnings("unchecked")
public static ItemStack[] extractRecipeItems(Object obj) {
if (obj instanceof ItemStack) return new ItemStack[] { (ItemStack) obj };
if (obj instanceof ItemStack[]) return (ItemStack[]) obj;
if (obj instanceof List) return ((List<ItemStack>) obj).toArray(new ItemStack[0]);
throw new ClassCastException(obj + " not an ItemStack, ItemStack[] or List<ItemStack?");
if (obj instanceof ItemStack item) {
return new ItemStack[] { item };
}
if (obj instanceof ItemStack[]arr) {
return arr;
}
if (obj instanceof FluidStack fluid) {
return new ItemStack[] { ItemFluidDisplay.createStack(fluid) };
}
if (obj instanceof FluidStack[]arr) {
final ItemStack[] items = new ItemStack[arr.length];
for (int i = 0; i < items.length; i++) {
items[i] = ItemFluidDisplay.createStack(arr[i]);
}
return items;
}
if (obj instanceof List list) {
final ItemStack[] items = new ItemStack[list.size()];
for (int i = 0; i < items.length; i++) {
items[i] = extractRecipeItem(list.get(i));
}
return items;
}
throw new ClassCastException(
obj + " not an ItemStack, ItemStack[], FluidStack, FluidStack[] or a List of them");
}
private static ItemStack extractRecipeItem(Object obj) {
if (obj == null) return null;
if (obj instanceof ItemStack item) return item;
if (obj instanceof FluidStack fluid) return ItemFluidDisplay.createStack(fluid);
throw new ClassCastException(obj + " not an ItemStack or FluidStack");
}
public static List<Integer> getRange(final int start, final int end) {
return new AbstractList<Integer>() {
return new AbstractList<>() {
@Override
public Integer get(int index) {
@@ -24,6 +24,7 @@ public final class PacketIDs {
public static final int REQUEST_POTION_GUI = 24;
public static final int SET_DUMMY_SLOT = 25;
public static final int SEND_CHAT_ITEM_LINK = 26;
public static final int SYNC = 27;
}
public static final class S2C {
@@ -37,5 +38,7 @@ public final class PacketIDs {
public static final int OPEN_ENCHANTMENT_GUI = 21;
public static final int SET_CREATIVE_PLUS_MODE = 23;
public static final int OPEN_POTION_GUI = 24;
public static final int CACHE_BOOKMARK_DATA = 25;
public static final int SYNC = 26;
}
}
+12 -17
View File
@@ -11,14 +11,12 @@ import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.api.GuiInfo;
import codechicken.nei.api.INEIGuiHandler;
import codechicken.nei.api.ShortcutInputHandler;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ? extends ItemsGrid.MouseContext>>
extends Widget implements IContainerTooltipHandler {
extends Widget {
protected static final int PADDING = 2;
@@ -84,10 +82,14 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
public abstract String getLabelText();
public abstract Rectangle4i calculateBounds();
public abstract Rectangle4i calculateBounds(GuiContainer gui);
protected static int snapDownToMultiple(int value, int multiple) {
return (value / multiple) * multiple;
}
public void resize(GuiContainer gui) {
final Rectangle4i bounds = calculateBounds();
final Rectangle4i bounds = calculateBounds(gui);
x = bounds.x;
y = bounds.y;
@@ -147,7 +149,6 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
LayoutManager.addWidget(pagePrev);
LayoutManager.addWidget(pageNext);
LayoutManager.addWidget(pageLabel);
this.grid.setVisible();
}
}
@@ -285,13 +286,7 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|| !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) {
if (button == 0) {
GuiCraftingRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy());
} else if (button == 1) {
GuiUsageRecipe.openRecipeGui("item", hoverSlot.getItemStack().copy());
}
ShortcutInputHandler.handleMouseClick(hoverSlot.getItemStack());
} else {
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1);
}
@@ -310,16 +305,16 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
}
@Override
public boolean handleKeyPress(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
final Point mouse = GuiDraw.getMousePosition();
if (!contains(mouse.x, mouse.y)) return false;
if (NEIClientConfig.isKeyHashDown("gui.next")) {
if (KeyManager.isKeyDown("gui.next")) {
this.grid.shiftPage(1);
return true;
}
if (NEIClientConfig.isKeyHashDown("gui.prev")) {
if (KeyManager.isKeyDown("gui.prev")) {
this.grid.shiftPage(-1);
return true;
}
@@ -0,0 +1,38 @@
package codechicken.nei;
import java.util.Collection;
import java.util.stream.Stream;
import org.apache.logging.log4j.Logger;
import codechicken.nei.api.IConfigureNEI;
final class PluginLoader {
private PluginLoader() {}
static void load(Stream<Class<?>> plugins, Collection<IConfigureNEI> loadedPlugins, Logger logger) {
final ClassLoader classLoader = IConfigureNEI.class.getClassLoader();
plugins.forEach(clazz -> {
final Thread thread = Thread.currentThread();
final ClassLoader previousClassLoader = thread.getContextClassLoader();
// Common-pool workers may use the system loader, which cannot see FML-only classes. Keep FML's loader
// through error logging too: old Log4j can otherwise throw while resolving a plugin's stack frames.
try {
thread.setContextClassLoader(classLoader);
try {
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
config.loadConfig();
loadedPlugins.add(config);
logger.debug("Loaded {}", clazz.getName());
} catch (Throwable e) {
logger.error("Failed to Load {}", clazz.getName(), e);
}
} finally {
thread.setContextClassLoader(previousClassLoader);
}
});
}
}
@@ -23,13 +23,18 @@ public class PopupInputHandler implements IContainerInputHandler {
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
if (NEIClientConfig.isKeyHashDown("gui.enchant") && canPerformAction("enchant")) {
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isHashDown("gui.enchant") && canPerformAction("enchant")) {
NEICPH.sendOpenEnchantmentWindow();
return true;
}
if (NEIClientConfig.isKeyHashDown("gui.potion") && canPerformAction("potion")) {
if (KeyManager.isHashDown("gui.potion") && canPerformAction("potion")) {
NEICPH.sendOpenPotionWindow();
return true;
}
@@ -4,30 +4,52 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.oredict.OreDictionary;
import org.lwjgl.opengl.GL11;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.item.FluidDrawer;
import codechicken.nei.item.FluidDrawer.FillDirection;
import codechicken.nei.recipe.Badge;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.StackInfo;
/**
* Simply an {@link ItemStack} with position. Mainly used in the recipe handlers.
*/
public class PositionedStack {
public class PositionedStack implements Cloneable {
/** Maximum chance value representing 100% probability (1 unit = 0.01%). */
public static final int CHANCE_FULL = 10_000;
public int relx;
public int rely;
public int width = 16;
public int height = 16;
public ItemStack[] items;
// compatibility dummy
public ItemStack item;
private boolean permutated = false;
protected int chance = CHANCE_FULL;
protected boolean permutated = false;
protected String acceptsLabel;
protected Function<ItemStack, List<String>> tooltip;
protected List<Badge> badges;
public PositionedStack(Object object, int x, int y, boolean genPerms) {
items = NEIServerUtils.extractRecipeItems(object);
@@ -82,14 +104,58 @@ public class PositionedStack {
for (ItemStack item : items) if (item.stackSize > i) item.stackSize = i;
}
public void setAcceptsLabel(String acceptsLabel) {
this.acceptsLabel = acceptsLabel;
}
public String getAcceptsLabel() {
return this.acceptsLabel;
}
public void setBadges(List<Badge> badges) {
this.badges = badges;
}
public List<Badge> getBadges() {
return this.badges;
}
public void setTooltip(List<String> tooltip) {
this.tooltip = tooltip == null ? null : item -> tooltip;
}
public void setTooltipProvider(Function<ItemStack, List<String>> provider) {
this.tooltip = provider;
}
public List<String> getTooltip() {
return this.tooltip == null ? null : this.tooltip.apply(this.item);
}
public int getChance() {
return this.chance;
}
public void setChance(int chance) {
this.chance = chance;
}
public PositionedStack copy() {
PositionedStack pStack = new PositionedStack(
Arrays.stream(this.items).map(ItemStack::copy).toArray(ItemStack[]::new),
relx,
rely,
false);
pStack.permutated = this.permutated;
return pStack;
try {
PositionedStack pStack = (PositionedStack) super.clone();
pStack.items = Arrays.stream(this.items).map(ItemStack::copy).toArray(ItemStack[]::new);
pStack.item = this.item == null ? null : this.item.copy();
pStack.badges = this.badges == null ? null : new ArrayList<>(this.badges);
return pStack;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public void draw(int mousex, int mousey) {
final int x = this.relx + (this.width - 16) / 2;
final int y = this.rely + (this.height - 16) / 2;
GuiContainerManager.drawItem(x, y, this.item);
}
public List<ItemStack> getFilteredPermutations() {
@@ -97,14 +163,14 @@ public class PositionedStack {
}
public List<ItemStack> getFilteredPermutations(ItemFilter additionalFilter) {
List<ItemStack> items = Arrays.asList(this.items);
List<ItemStack> items = new ArrayList<>(Arrays.asList(this.items));
items = filteringPermutations(items, item -> !ItemInfo.isHidden(item));
items = filteringPermutations(items, PresetsList.getItemFilter());
items = filteringPermutations(items, GuiRecipe.getSearchItemFilter());
items = filteringPermutations(items, additionalFilter);
items.sort(Comparator.comparing(FavoriteRecipes::contains).reversed());
items.sort(Comparator.comparing(FavoriteRecipes::containsManual).reversed());
return items;
}
@@ -114,16 +180,25 @@ public class PositionedStack {
return filteredItems.isEmpty() ? items : filteredItems;
}
public boolean setPermutationToRender(ItemStack ingredient) {
public int getPermutationIndex(ItemStack stack) {
for (int index = 0; index < this.items.length; index++) {
if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(this.items[index], ingredient)) {
setPermutationToRender(index);
return true;
if (NEIServerUtils.areStacksSameType(items[index], stack)) {
return index;
}
}
return false;
return -1;
}
public boolean setPermutationToRender(ItemStack ingredient) {
final int stackIndex = getPermutationIndex(ingredient);
if (stackIndex >= 0) {
setPermutationToRender(stackIndex);
}
return stackIndex >= 0;
}
public void setPermutationToRender(int index) {
@@ -136,6 +211,12 @@ public class PositionedStack {
}
}
public boolean contains(int mx, int my) {
return mx >= this.relx - 1 && mx < this.relx + this.width + 1
&& my >= this.rely - 1
&& my < this.rely + this.height + 1;
}
public boolean contains(ItemStack ingredient) {
for (ItemStack item : items) if (NEIServerUtils.areStacksSameTypeCrafting(item, ingredient)) return true;
@@ -161,4 +242,143 @@ public class PositionedStack {
public String toString() {
return "PositionedStack(output='" + item.toString() + "')";
}
public static class Placeholder extends PositionedStack {
public Placeholder(Object object, int x, int y, boolean genPerms) {
super(object, x, y, genPerms);
}
public Placeholder(Object object, int x, int y) {
this(object, x, y, true);
}
@Override
public void draw(int mousex, int mousey) {}
}
public static class Tank extends PositionedStack {
/** The tank size the fill level is measured against, in mB. 0 means "always render full". */
protected int capacity = 0;
protected boolean flowingTexture = false;
protected FillDirection fillDirection = FillDirection.AUTO;
protected DrawableResource overlay;
private ItemStack cachedFluidItem;
private FluidStack cachedFluidStack;
public Tank(Object object, int x, int y, boolean genPerms) {
super(object, x, y, genPerms);
}
public Tank(Object object, int x, int y) {
this(object, x, y, true);
}
public Tank(Object object, int x, int y, int width, int height, int capacity) {
super(object, x, y, false);
this.width = width;
this.height = height;
setCapacity(capacity);
}
public int getCapacity() {
return this.capacity;
}
public Tank setCapacity(int capacity) {
this.capacity = Math.max(capacity, 0);
return this;
}
public boolean hasFlowingTexture() {
return this.flowingTexture;
}
public Tank setFlowingTexture(boolean flowingTexture) {
this.flowingTexture = flowingTexture;
return this;
}
public FillDirection getFillDirection() {
return this.fillDirection;
}
public Tank setFillDirection(FillDirection fillDirection) {
this.fillDirection = fillDirection == null ? FillDirection.AUTO : fillDirection;
return this;
}
public DrawableResource getOverlay() {
return this.overlay;
}
public Tank setOverlay(String resourceLocation, int u, int v, int width, int height) {
return setOverlay(new DrawableBuilder(resourceLocation, u, v, width, height).build());
}
public Tank setOverlay(DrawableResource overlay) {
this.overlay = overlay;
return this;
}
protected FluidStack getFluidStack() {
if (this.item != this.cachedFluidItem) {
this.cachedFluidItem = this.item;
this.cachedFluidStack = StackInfo.isFluidDisplayItem(this.item) ? StackInfo.getFluid(this.item) : null;
}
return this.cachedFluidStack;
}
@Override
public List<String> getTooltip() {
final List<String> tooltip = new ArrayList<>();
final List<String> customTooltip = super.getTooltip();
final FluidStack fluidStack = getFluidStack();
if (fluidStack != null) {
tooltip.add(
EnumChatFormatting.GRAY + NEIClientUtils
.translate("recipe.fluid.tank.amount", NEIClientUtils.formatFluid(fluidStack.amount)));
}
if (customTooltip != null) {
tooltip.addAll(customTooltip);
}
return tooltip;
}
@Override
public void draw(int mousex, int mousey) {
final FluidStack fluidStack = getFluidStack();
if (fluidStack == null) {
super.draw(mousex, mousey);
return;
}
FluidDrawer.drawTank(
this.relx,
this.rely,
this.width,
this.height,
this.capacity,
fluidStack,
this.flowingTexture,
this.fillDirection);
if (this.overlay != null) {
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL11.GL_LIGHTING);
this.overlay.draw(this.relx, this.rely);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_BLEND);
}
}
}
}
@@ -83,17 +83,20 @@ public class PresetsList {
}
@Override
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others) {
public boolean matches(IRecipeHandler handler, int recipeIndex) {
if (matchPositionedStack(ingredients, false)) {
if (matchPositionedStack(handler.getIngredientStacks(recipeIndex), false)) {
return false;
}
final PositionedStack result = handler.getResultStack(recipeIndex);
if (result != null && matchPositionedStack(result)) {
return true;
}
final List<PositionedStack> others = handler.getOtherStacks(recipeIndex);
if (!others.isEmpty() && matchPositionedStack(others, true)) {
return true;
}
@@ -160,9 +163,7 @@ public class PresetsList {
public static void load() {
final File presetsFile = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/presets.ini");
if (presetsFile == null || !presetsFile.exists()) {
return;
}
if (!presetsFile.exists()) return;
List<String> itemStrings;
try (FileInputStream reader = new FileInputStream(presetsFile)) {
@@ -248,7 +249,7 @@ public class PresetsList {
updateSubsets();
CollapsibleItems.saveStates();
CollapsibleItems.load();
LayoutManager.markItemsDirty();
ItemList.refreshItems.restart();
}
private static void updateSubsets() {
@@ -20,9 +20,12 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
@Override
protected void initInternalTextField() {
field = new GuiSearchField();
final GuiSearchField field = new GuiSearchField();
field.setMaxStringLength(maxSearchLength);
field.setCursorPositionZero();
field.setSkipRecipeTokens(true);
((SearchTextFormatter) field.getFormatter()).setContextToken("<>");
this.field = field;
}
public boolean isVisible() {
@@ -45,15 +48,19 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
if (focused() && NEIClientConfig.isKeyHashDown("gui.getprevioussearch")) {
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && NEIClientConfig.isKeyHashDown("gui.getnextsearch")) {
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
return true;
}
return false;
}
@Override
+34 -17
View File
@@ -13,13 +13,12 @@ import net.minecraft.util.EnumChatFormatting;
import net.minecraftforge.fluids.FluidStack;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
import codechicken.nei.SearchTokenParser.SearchMode;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.filter.NothingItemFilter;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.TextHistory;
@@ -85,14 +84,15 @@ public class SearchField extends TextField implements ItemFilterProvider {
}
@Override
public SearchMode getSearchMode() {
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search." + this.name + "SearchMode"));
public String getName() {
return this.name;
}
}
public static class GuiSearchField extends FormattedTextField implements ItemFilterProvider, IRecipeFilterProvider {
protected final SearchTokenParser searchParser;
protected boolean skipRecipeTokens = false;
public GuiSearchField() {
this(SearchField.searchParser);
@@ -106,6 +106,10 @@ public class SearchField extends TextField implements ItemFilterProvider {
setPlaceholder(NEIClientUtils.translate("inventory.search.placeholder"));
}
public void setSkipRecipeTokens(boolean skipRecipeTokens) {
this.skipRecipeTokens = skipRecipeTokens;
}
@Override
public ItemFilter getFilter() {
return getFilter(getText());
@@ -117,7 +121,7 @@ public class SearchField extends TextField implements ItemFilterProvider {
}
public ItemFilter getFilter(String filterText) {
return this.searchParser.getFilter(filterText);
return this.searchParser.getFilter(filterText, this.skipRecipeTokens);
}
public IRecipeFilter getRecipeFilter(String filterText) {
@@ -217,25 +221,30 @@ public class SearchField extends TextField implements ItemFilterProvider {
public void onTextChange(String oldText) {
final String newText = text();
if (!newText.equals(oldText)) {
if (newText.length() > 0) NEIClientConfig.logger.debug("Searching for " + newText);
NEIClientConfig.setSearchExpression(newText);
ItemList.updateFilter.restart();
}
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
if (isVisible() && NEIClientConfig.isKeyHashDown("gui.search")) {
if (isVisible() && KeyManager.isHashDown("gui.search")) {
setFocus(true);
}
if (focused() && NEIClientConfig.isKeyHashDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && NEIClientConfig.isKeyHashDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
return true;
}
return false;
}
@Override
@@ -310,6 +319,9 @@ public class SearchField extends TextField implements ItemFilterProvider {
public static String getEscapedSearchText(String text) {
text = EnumChatFormatting.getTextWithoutFormattingCodes(text);
boolean addQuotes = text.contains(" ")
&& NEIClientConfig.getBooleanSetting("inventory.search.quoteDropItemName");
switch (NEIClientConfig.getIntSetting("inventory.search.patternMode")) {
case 1:
text = text.replaceAll("[\\?|\\*]", "\\\\$0");
@@ -318,14 +330,19 @@ public class SearchField extends TextField implements ItemFilterProvider {
text = text.replaceAll("[{}()\\[\\].+*?^$\\\\|]", "\\\\$0");
break;
case 3:
text = text.replaceAll("\"", "\\\\$0");
text = "\"" + text + "\"";
return text;
// In extended+ quotes require only quotes to be escaped
if (addQuotes) {
text = text.replaceAll("\"", "\\\\$0");
} else {
String prefixes = searchParser.getPrefixes();
text = text.replaceAll("[-<>^{}|/\\\\\"" + Pattern.quote(prefixes) + "]", "\\\\$0");
}
break;
default:
break;
}
if (text.contains(" ") && NEIClientConfig.getBooleanSetting("inventory.search.quoteDropItemName")) {
if (addQuotes) {
text = "\"" + text + "\"";
}
@@ -1,23 +1,26 @@
package codechicken.nei;
import java.util.List;
import java.util.StringJoiner;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.FormattedTextField.TextFormatter;
import codechicken.nei.SearchTokenParser.SearchToken;
import codechicken.nei.search.SearchExpressionFormatVisitor;
import codechicken.nei.search.SearchExpressionUtils;
import codechicken.nei.search.SearchToken;
public class SearchTextFormatter implements TextFormatter {
protected final SearchTokenParser searchParser;
protected String contextToken = "";
public SearchTextFormatter(SearchTokenParser searchParser) {
this.searchParser = searchParser;
}
public void setContextToken(String contextToken) {
this.contextToken = contextToken;
}
@Override
public String format(String text) {
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
if (patternMode == 3) {
@@ -26,50 +29,65 @@ public class SearchTextFormatter implements TextFormatter {
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
}
final SearchExpressionFormatVisitor visitor = new SearchExpressionFormatVisitor(searchParser);
return SearchExpressionUtils.visitSearchExpression(text, visitor);
return SearchExpressionUtils.visitRecipeSearchExpression(text, visitor);
} else {
final String[] parts = (text + "| ").split("\\|");
StringJoiner formattedText = new StringJoiner(SearchExpressionUtils.HIGHLIGHTS.OR + "|");
final StringBuilder formattedText = new StringBuilder();
for (int i = 0; i < parts.length - 1; i++) {
final String filterText = parts[i];
final List<SearchToken> tokens = searchParser.splitSearchText(filterText);
StringBuilder formattedPart = new StringBuilder();
int startIndex = 0;
for (String[] orQuery : searchParser.splitByDelimiters(text, "|", false)) {
final String filterText = orQuery[1];
for (SearchToken token : tokens) {
formattedPart.append(filterText.substring(startIndex, token.start));
EnumChatFormatting tokenColor = EnumChatFormatting.RESET;
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.OR + orQuery[0]);
if (token.firstChar != null) {
tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor();
for (String[] contextPart : searchParser.splitByDelimiters(filterText, this.contextToken, false)) {
if (!contextPart[0].isEmpty()) {
formattedText.append(
SearchExpressionUtils.HIGHLIGHTS.RECIPE + contextPart[0].substring(0, 1)
+ EnumChatFormatting.RESET);
contextPart[1] = contextPart[0].substring(1) + contextPart[1];
}
if (token.ignore) {
formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.NEGATE + "-");
if (contextPart[1].isEmpty()) {
continue;
}
if (token.firstChar != null) {
formattedPart.append(tokenColor + String.valueOf(token.firstChar));
int startIndex = 0;
for (SearchToken token : searchParser.splitSearchText(contextPart[1])) {
formattedText.append(contextPart[1].substring(startIndex, token.start));
EnumChatFormatting tokenColor = EnumChatFormatting.RESET;
if (token.firstChar != null) {
tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor();
}
if (token.ignore != null) {
formattedText
.append(SearchExpressionUtils.HIGHLIGHTS.NEGATE + String.valueOf(token.ignore));
}
if (token.firstChar != null) {
formattedText.append(tokenColor + String.valueOf(token.firstChar));
}
if (token.quotes) {
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
}
if (!token.rawText.isEmpty()) {
formattedText.append(tokenColor + token.rawText);
}
if (token.quotes) {
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
}
startIndex = token.end;
}
if (token.quotes) {
formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
}
if (!token.rawText.isEmpty()) {
formattedPart.append(tokenColor + token.rawText);
}
if (token.quotes) {
formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
}
startIndex = token.end;
formattedText.append(contextPart[1].substring(startIndex, contextPart[1].length()));
}
formattedPart.append(filterText.substring(startIndex, filterText.length()));
formattedText.add(formattedPart);
}
return formattedText.toString();
@@ -1,33 +1,30 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.Language;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.ItemList.AllMultiItemFilter;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.EverythingItemFilter;
import codechicken.nei.api.IRecipeFilter;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.filter.AllMultiItemFilter;
import codechicken.nei.filter.AnyItemRecipeFilter;
import codechicken.nei.filter.AnyMultiItemFilter;
import codechicken.nei.filter.EverythingItemFilter;
import codechicken.nei.filter.NegatedItemFilter;
import codechicken.nei.filter.NothingItemFilter;
import codechicken.nei.filter.AllMultiRecipeFilter;
import codechicken.nei.filter.AnyMultiRecipeFilter;
import codechicken.nei.filter.RecipeFilter;
import codechicken.nei.filter.RecipeFilter.FilterContext;
import codechicken.nei.search.ItemFilterVisitor;
import codechicken.nei.search.RecipeFilterVisitor;
import codechicken.nei.search.SearchExpressionUtils;
import codechicken.nei.search.SearchToken;
public class SearchTokenParser {
@@ -46,20 +43,6 @@ public class SearchTokenParser {
}
}
public static class SearchToken {
public boolean ignore = false;
public boolean quotes = false;
public Character firstChar = null;
public String[] words;
public String rawText = "";
public int start = 0;
public int end = 0;
}
public static interface ISearchParserProvider {
public ItemFilter getFilter(String searchText);
@@ -76,7 +59,13 @@ public class SearchTokenParser {
public EnumChatFormatting getHighlightedColor();
public SearchMode getSearchMode();
default SearchMode getSearchMode() {
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search." + getName() + "SearchMode"));
}
default String getName() {
return String.valueOf(getPrefix());
}
}
private static class ProvidersCache {
@@ -109,41 +98,38 @@ public class SearchTokenParser {
public static final Pattern SPACE_PATTERN = Pattern.compile(" ");
protected final LRUCache<String, ItemFilter> filtersCache = new LRUCache<>(20);
protected final LRUCache<String, IRecipeFilter> recipesCache = new LRUCache<>(20);
protected final List<ISearchParserProvider> searchProviders;
protected final Set<Character> redefinedPrefixes = new HashSet<>();
protected final ProvidersCache providersCache = new ProvidersCache();
protected final Map<Character, Character> prefixRedefinitions = new HashMap<>();
protected String prefixes = null;
public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
this.searchProviders = searchProviders;
updateRedefinedPrefixes();
}
public SearchTokenParser() {
this(new ArrayList<>());
updateRedefinedPrefixes();
}
public void addProvider(ISearchParserProvider provider) {
this.searchProviders.add(provider);
updateRedefinedPrefixes(provider);
clearCache();
}
public synchronized void clearCache() {
this.providersCache.clear();
this.filtersCache.clear();
this.recipesCache.clear();
this.prefixes = null;
}
public void clearCache() {
this.filtersCache.clear();
updateRedefinedPrefixes();
}
protected List<ISearchParserProvider> getProviders() {
public List<ISearchParserProvider> getProviders() {
Language currentLanguage = Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage();
if (!currentLanguage.getLanguageCode().equals(providersCache.languageCode)) {
providersCache.providers = new ArrayList<>();
providersCache.languageCode = currentLanguage.getLanguageCode();
Map<Character, ISearchParserProvider> providers = new HashMap<>();
final List<ISearchParserProvider> providersList = new ArrayList<>();
final Map<Character, ISearchParserProvider> providers = new HashMap<>();
for (int index = this.searchProviders.size() - 1; index >= 0; index--) {
ISearchParserProvider provider = this.searchProviders.get(index);
@@ -152,19 +138,21 @@ public class SearchTokenParser {
if (provider.getSearchMode() == SearchMode.PREFIX && !providers.containsKey(provider.getPrefix())) {
providers.put(provider.getPrefix(), provider);
} else if (provider.getSearchMode() == SearchMode.ALWAYS) {
providersCache.providers.add(provider);
providersList.add(provider);
}
}
}
providersCache.providers.addAll(providers.values());
providersList.addAll(providers.values());
providersCache.languageCode = currentLanguage.getLanguageCode();
providersCache.providers = providersList;
}
return providersCache.providers;
}
public boolean hasRedefinedPrefix(char ch) {
return redefinedPrefixes.contains(ch);
return getPrefixes().indexOf(ch) >= 0;
}
public ISearchParserProvider getProvider(char ch) {
@@ -191,18 +179,55 @@ public class SearchTokenParser {
}
public synchronized ItemFilter getFilter(String filterText) {
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
if (filterText == null || filterText.isEmpty()) {
return getFilter(filterText, false);
}
public synchronized ItemFilter getFilter(String rawText, boolean skipRecipeTokens) {
final String filterText = EnumChatFormatting.getTextWithoutFormattingCodes(rawText).toLowerCase();
if (filterText.isEmpty()) {
return new EverythingItemFilter();
}
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
if (patternMode != 3) {
return this.filtersCache.computeIfAbsent(filterText, text -> {
final String[] parts = text.split("\\|");
final List<ItemFilter> searchTokens = Arrays.stream(parts).map(this::parseSearchText)
.filter(s -> s != null).collect(Collectors.toList());
return this.filtersCache.computeIfAbsent(skipRecipeTokens + ":" + filterText, _t -> {
final List<ItemFilter> searchTokens = new ArrayList<>();
for (String[] subQuery : splitByDelimiters(filterText, "|", false)) {
if (subQuery[1].isEmpty()) {
continue;
}
final List<ItemFilter> tokens = new ArrayList<>();
for (String[] contextQuery : splitByDelimiters(subQuery[1], skipRecipeTokens ? "<>" : "", false)) {
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")) {
continue;
}
if (!contextQuery[0].isEmpty()) {
contextQuery[0] = contextQuery[0].substring(1);
}
for (SearchToken token : splitSearchText(contextQuery[0] + contextQuery[1])) {
final ItemFilter result = token.getFilter(this);
if (result != null) {
tokens.add(result);
}
}
}
if (tokens.size() == 1) {
searchTokens.add(tokens.get(0));
} else if (!tokens.isEmpty()) {
searchTokens.add(new AllMultiItemFilter(tokens));
}
}
if (searchTokens.isEmpty()) {
return new EverythingItemFilter();
@@ -213,183 +238,260 @@ public class SearchTokenParser {
}
});
} else {
if (spaceMode == 1) {
filterText = SearchTokenParser.SPACE_PATTERN.matcher(filterText).replaceAll("\\\\ ");
}
return this.filtersCache.computeIfAbsent(filterText, text -> {
final ItemFilterVisitor visitor = new ItemFilterVisitor(this);
final ItemFilter searchToken = SearchExpressionUtils.visitSearchExpression(text, visitor);
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
return new IsRegisteredItemFilter(searchToken);
if (spaceMode == 1) {
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
}
if (skipRecipeTokens) {
return new IsRegisteredItemFilter(
SearchExpressionUtils.visitRecipeSearchExpression(text, new ItemFilterVisitor(this)));
} else {
return new IsRegisteredItemFilter(
SearchExpressionUtils.visitSearchExpression(text, new ItemFilterVisitor(this)));
}
});
}
}
public synchronized IRecipeFilter getRecipeFilter(String filterText) {
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
if (patternMode != 3) {
return new AnyItemRecipeFilter(getFilter(filterText));
}
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
if (filterText == null || filterText.isEmpty()) {
return new AnyItemRecipeFilter(new EverythingItemFilter());
}
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
if (spaceModeEnabled == 1) {
filterText = SearchTokenParser.SPACE_PATTERN.matcher(filterText).replaceAll("\\\\ ");
if (filterText.isEmpty()) {
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
}
final RecipeFilterVisitor visitor = new RecipeFilterVisitor(this);
return SearchExpressionUtils.visitSearchExpression(filterText, visitor);
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
if (patternMode != 3) {
return this.recipesCache.computeIfAbsent(filterText, input -> {
final List<IRecipeFilter> searchTokens = new ArrayList<>();
for (String[] orQueryPart : splitByDelimiters(input, "|", false)) {
if (orQueryPart[1].isEmpty()) {
continue;
}
final List<IRecipeFilter> contextList = new ArrayList<>();
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], "<>", false)) {
if (contextQuery[1].isEmpty()) {
continue;
}
final List<IRecipeFilter> tokens = new ArrayList<>();
FilterContext context = FilterContext.ANY;
if (!contextQuery[0].isEmpty()) {
context = FilterContext.fromChar(contextQuery[0].charAt(0));
contextQuery[0] = contextQuery[0].substring(1);
}
for (SearchToken token : splitSearchText(contextQuery[0] + contextQuery[1])) {
final ItemFilter result = token.getFilter(this);
if (result != null) {
tokens.add(
new RecipeFilter(context, token.ignore == null || token.ignore != '!', result));
}
}
if (tokens.size() == 1) {
contextList.add(tokens.get(0));
} else if (!tokens.isEmpty()) {
contextList.add(new AllMultiRecipeFilter(tokens));
}
}
if (contextList.size() == 1) {
searchTokens.add(contextList.get(0));
} else if (!contextList.isEmpty()) {
searchTokens.add(new AllMultiRecipeFilter(contextList));
}
}
if (searchTokens.isEmpty()) {
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
} else if (searchTokens.size() == 1) {
return searchTokens.get(0);
} else {
return new AnyMultiRecipeFilter(searchTokens);
}
});
} else {
return this.recipesCache.computeIfAbsent(filterText, text -> {
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
if (spaceMode == 1) {
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
}
return SearchExpressionUtils.visitRecipeSearchExpression(text, new RecipeFilterVisitor(this));
});
}
}
public List<SearchToken> splitSearchText(String filterText) {
if (filterText.isEmpty()) {
return Collections.emptyList();
}
final List<SearchToken> tokens = new ArrayList<>();
final String prefixes = getPrefixes();
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
// The regular expression first tries to match a string that starts with a space or the beginning of the string,
// followed by a sequence of characters that do not contain special characters -"@#$%&, and ends with a space
// and the characters -"@#$%& or the end of the string.
final String patternPart1 = "(?<tokenA>(^|\\s+)(?:[^" + Pattern.quote(" -\"" + prefixes)
+ "]).+?(?=\\s+["
+ Pattern.quote("-\"" + prefixes)
+ "]|$))";
// If the first condition is not met, it tries to match a sequence of - characters, followed by @#$%&
// characters, then either a quoted string or non-space characters.
final String patternPart2 = "((?<ignore>-*)(?<firstChar>[" + Pattern.quote(prefixes)
+ "]*)(?<tokenB>\\\".*?(?:\\\"|$)|\\S+\\s*))";
final Pattern pattern = Pattern.compile(spaceMode == 0 ? patternPart2 : (patternPart1 + "|" + patternPart2));
final Matcher filterMatcher = pattern.matcher(filterText);
final String prefixes = getPrefixes();
SearchToken lastToken = null;
int lastEnd = 0;
while (filterMatcher.find()) {
String firstChar = filterMatcher.group("firstChar");
SearchToken token = new SearchToken();
token.start = filterMatcher.start();
token.end = filterMatcher.end();
token.ignore = "-".equals(filterMatcher.group("ignore"));
token.rawText = spaceMode == 0 ? null : filterMatcher.group("tokenA");
for (String[] part : splitByDelimiters(filterText, "-!" + prefixes, true)) {
final SearchToken token = createToken(lastEnd, part, prefixes);
final int length = part[0].length() + part[1].length();
if (firstChar != null && !firstChar.isEmpty()) {
token.firstChar = firstChar.charAt(0);
if (token.isSimpleFilter() && token.rawText.isEmpty()) {
lastEnd += length;
continue;
}
if (token.rawText == null) { // spaceMode == 0
token.rawText = filterMatcher.group("tokenB");
token.quotes = token.rawText.length() > 1 && token.rawText.startsWith("\"")
&& token.rawText.endsWith("\"");
if (spaceMode == 0) { // and
tokens.add(lastToken = token);
} else if (spaceMode == 1) { // space
if (token.quotes) {
token.rawText = token.rawText.substring(1, token.rawText.length() - 1);
if (lastToken != null && lastToken.isSimpleFilter() && token.isSimpleFilter()) {
lastToken.rawText = filterText.substring(lastToken.start, token.end).trim();
lastToken.words[0] = lastToken.rawText;
lastToken.end = token.end;
} else {
tokens.add(lastToken = token);
}
} else if (spaceMode == 2) { // and-x
if (lastToken != null && lastToken.isSimpleFilter() && token.isSimpleFilter()) {
final String[] newWords = new String[lastToken.words.length + token.words.length];
System.arraycopy(lastToken.words, 0, newWords, 0, lastToken.words.length);
System.arraycopy(token.words, 0, newWords, lastToken.words.length, token.words.length);
lastToken.words = newWords;
lastToken.rawText = filterText.substring(lastToken.start, token.end).trim();
lastToken.end = token.end;
} else {
tokens.add(lastToken = token);
}
token.words = new String[] { token.rawText.trim() };
} else if (spaceMode == 2) {
token.words = token.rawText.trim().split("\\s+");
} else {
token.words = new String[] { token.rawText.trim() };
}
tokens.add(token);
lastEnd += length;
}
return tokens;
}
private String getPrefixes() {
final StringBuilder builder = new StringBuilder();
for (Character redefinedPrefix : redefinedPrefixes) {
builder.append(redefinedPrefix);
private SearchToken createToken(int lastEnd, String[] part, String prefixes) {
final SearchToken token = new SearchToken();
String pref = part[0];
String query = part[1].trim();
token.start = lastEnd;
token.end = lastEnd + pref.length() + query.length();
if (pref.startsWith("-")) {
token.ignore = '-';
pref = pref.substring(1);
} else if (pref.startsWith("!")) {
token.ignore = '!';
pref = pref.substring(1);
}
return builder.toString();
if (!pref.isEmpty()) {
char firstChar = pref.charAt(0);
if (prefixes.indexOf(firstChar) >= 0) {
token.firstChar = firstChar;
pref = pref.substring(1);
}
}
String text = pref + query;
token.quotes = text.length() > 1 && text.startsWith("\"") && text.endsWith("\"");
if (token.quotes) {
text = text.substring(1, text.length() - 1);
}
token.rawText = text;
token.words = new String[] { token.quotes ? text.replaceAll("\\\\\"", "\"") : text };
return token;
}
public List<String[]> splitByDelimiters(String input, String delimiters, boolean space) {
if (delimiters == null || delimiters.isEmpty()) {
return Collections.singletonList(new String[] { "", input });
}
final List<String[]> tokens = new ArrayList<>();
final int length = input.length();
boolean insideQuotes = false;
String token = "";
int lastEnd = 0;
int index = 0;
while (index < length) {
final char ch = input.charAt(index);
if (!insideQuotes && delimiters.indexOf(ch) >= 0
&& (!space || index == 0 || input.charAt(index - 1) == ' ' || lastEnd == index)) {
if (lastEnd == index) {
token += String.valueOf(ch);
} else {
tokens.add(new String[] { token, input.substring(lastEnd, index) });
token = String.valueOf(ch);
}
lastEnd = index + 1;
} else if (!insideQuotes && space && ch != ' ' && (index > 0 && input.charAt(index - 1) == ' ')) {
tokens.add(new String[] { token, input.substring(lastEnd, index) });
token = "";
lastEnd = index;
}
if (ch == '"' && (index == 0 || input.charAt(index - 1) != '\\')) {
insideQuotes = !insideQuotes;
}
index++;
}
tokens.add(new String[] { token, input.substring(lastEnd) });
return tokens;
}
public String getPrefixes() {
if (this.prefixes == null) {
final StringBuilder builder = new StringBuilder();
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchTokenParser.SearchMode.PREFIX) {
builder.append(getRedefinedPrefix(provider.getPrefix()));
}
}
this.prefixes = builder.toString();
}
return this.prefixes;
}
public char getRedefinedPrefix(char prefix) {
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
}
public void updateRedefinedPrefixes() {
this.redefinedPrefixes.clear();
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchTokenParser.SearchMode.PREFIX) {
redefinedPrefixes.add(getRedefinedPrefix(provider.getPrefix()));
}
}
this.redefinedPrefixes.add('\0');
}
public void updateRedefinedPrefixes(ISearchParserProvider provider) {
if (provider.getSearchMode() == SearchMode.PREFIX) {
this.redefinedPrefixes.add(getRedefinedPrefix(provider.getPrefix()));
}
}
private ItemFilter parseSearchText(String filterText) {
if (filterText.isEmpty()) {
return null;
}
final AllMultiItemFilter searchTokens = new AllMultiItemFilter();
final List<SearchToken> tokens = splitSearchText(filterText);
for (SearchToken token : tokens) {
if (!token.rawText.isEmpty()) {
ItemFilter result = parseToken(token);
if (token.ignore) {
searchTokens.filters.add(new NegatedItemFilter(result));
} else {
searchTokens.filters.add(result);
}
} else if (!token.ignore) {
searchTokens.filters.add(new NothingItemFilter());
}
}
return searchTokens;
}
private ItemFilter parseToken(SearchToken token) {
final ISearchParserProvider provider = token.firstChar == null ? null : this.getProvider(token.firstChar);
if (provider == null || provider.getSearchMode() == SearchMode.NEVER) {
final List<ItemFilter> filters = new ArrayList<>();
for (ISearchParserProvider _provider : getProviders()) {
if (_provider.getSearchMode() == SearchMode.ALWAYS) {
AllMultiItemFilter filter = generateFilters(_provider, token.words);
if (!filter.filters.isEmpty()) {
filters.add(filter);
}
}
}
return filters.isEmpty() ? new NothingItemFilter() : new AnyMultiItemFilter(filters);
} else {
return generateFilters(provider, token.words);
}
}
private AllMultiItemFilter generateFilters(ISearchParserProvider provider, String[] words) {
final AllMultiItemFilter filters = new AllMultiItemFilter();
for (String work : words) {
final ItemFilter filter = provider.getFilter(work);
if (filter != null) {
filters.filters.add(filter);
}
}
return filters;
}
}
@@ -10,6 +10,7 @@ import net.minecraftforge.event.world.WorldEvent;
import codechicken.core.CommonUtils;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.item.ItemFluidDisplay;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerChangedDimensionEvent;
@@ -18,13 +19,12 @@ import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent;
import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerRespawnEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
import cpw.mods.fml.common.registry.GameRegistry;
public class ServerHandler {
private static ServerHandler instance;
public static void load() {
instance = new ServerHandler();
ServerHandler instance = new ServerHandler();
PacketCustom.assignHandler(NEICPH.channel, new NEISPH());
FMLCommonHandler.instance().bus().register(instance);
@@ -33,6 +33,14 @@ public class ServerHandler {
NEIActions.init();
}
public static void preInit() {
registerFluidDisplayItem();
}
private static void registerFluidDisplayItem() {
GameRegistry.registerItem(new ItemFluidDisplay(), "neiFluidDisplay");
}
@SubscribeEvent
public void tickEvent(TickEvent.WorldTickEvent event) {
if (event.phase == Phase.START && !event.world.isRemote
@@ -48,8 +56,7 @@ public class ServerHandler {
@SubscribeEvent
public void tickEvent(TickEvent.PlayerTickEvent event) {
if (event.phase == Phase.START && event.player instanceof EntityPlayerMP) {
EntityPlayerMP player = (EntityPlayerMP) event.player;
if (event.phase == Phase.START && event.player instanceof EntityPlayerMP player) {
PlayerSave save = NEIServerConfig.forPlayer(player.getCommandSenderName());
if (save == null) return;
updateMagneticPlayer(player, save);
@@ -0,0 +1,36 @@
package codechicken.nei;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.PacketIDs.C2S;
/**
* Waits until the server has processed everything the client sent before. The server handles slot clicks and NEI
* packets in the order they came and answers after it has sent the slot updates, so once the answer is here the client
* container holds what the server computed.
*/
public final class ServerSync {
private static int lastId;
private static volatile int awaiting;
private ServerSync() {}
public static void request() {
if (!NEIClientConfig.hasSMPCounterPart()) {
awaiting = 0;
return;
}
awaiting = ++lastId;
new PacketCustom(NEICPH.channel, C2S.SYNC).writeInt(awaiting).sendToServer();
}
public static boolean pending() {
return awaiting != 0;
}
static void onReply(int id) {
if (id == awaiting) awaiting = 0;
}
}
+101 -127
View File
@@ -6,23 +6,19 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.util.EnumChatFormatting;
import org.lwjgl.opengl.GL11;
@@ -32,15 +28,14 @@ import com.google.gson.JsonParser;
import codechicken.core.gui.GuiScrollSlot;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
import codechicken.nei.SearchTokenParser.SearchMode;
import codechicken.nei.api.API;
import codechicken.nei.api.ItemFilter;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.filter.AnyMultiItemFilter;
import codechicken.nei.filter.NothingItemFilter;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.ICraftingHandler;
@@ -49,15 +44,13 @@ import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
import codechicken.nei.util.NEIMouseUtils;
public class SubsetWidget extends Button implements ItemFilterProvider, IContainerTooltipHandler {
public class SubsetWidget extends Button implements ItemFilterProvider {
protected static final int SLOT_HEIGHT = 18;
protected static final int MARGIN = 2;
protected static final char PREFIX = '%';
protected static ReentrantReadWriteLock hiddenItemLock = new ReentrantReadWriteLock();
protected static Lock hiddenWriteLock = hiddenItemLock.writeLock();
protected static Lock hiddenReadLock = hiddenItemLock.readLock();
private static final Object hiddenLock = new Object();
public static class SubsetTag {
@@ -167,7 +160,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
// 1 - partitial hidden
// 2 - enabled
public int state = 2;
public int calculatedWidth;
public volatile int calculatedWidth = -1;
public final List<ItemStack> items;
public final List<SubsetTag> children = new ArrayList<>();
protected final SubsetSlot slot = new SubsetSlot();
@@ -213,7 +206,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
this.state = 2;
this.items.clear();
this.children.clear();
this.calculatedWidth = 0;
this.calculatedWidth = -1;
}
public void updateVisiblity(int mx, int my) {
@@ -268,6 +261,10 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
public void resize(Rectangle4i screen, Rectangle4i parent, boolean dropRight) {
if (calculatedWidth < 0) {
calculatedWidth = this.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0);
}
int height = Math.min(slot.contentHeight(), screen.h);
int width = Math.max(calculatedWidth + 2, this.items.isEmpty() ? 0 : 16 + MARGIN * 2);
int scrollbarWidth = slot.scrollbarDim().width;
@@ -379,11 +376,18 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
final AnyMultiItemFilter filter = new AnyMultiItemFilter();
final Set<ItemStack> filteredItems = new HashSet<>();
final List<SubsetTag> snapshot;
for (SubsetTag tag : tags.values()) {
synchronized (tags) {
snapshot = new ArrayList<>(tags.values());
}
for (SubsetTag tag : snapshot) {
if (tag.filter != null && matches(tag.path, searchText, pattern)) {
filteredItems.addAll(tag.items);
filter.filters.add(tag.filter);
synchronized (tag.items) {
filteredItems.addAll(tag.items);
filter.filters.add(tag.filter);
}
}
}
@@ -399,8 +403,8 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
@Override
public SearchMode getSearchMode() {
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search.subsetsSearchMode"));
public String getName() {
return "subsets";
}
private boolean matches(String name, String searchText, Pattern pattern) {
@@ -413,9 +417,11 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
/**
* All operations on this variable should be synchronised.
* Canonical {@link StackInfo#getItemStackGUID(ItemStack)} of every hidden item, stored by GUID so the state
* survives world reloads. All operations should be synchronised.
*/
private static final ItemStackSet hiddenItems = new ItemStackSet();
private static final Set<String> hiddenItems = new HashSet<>();
private static final Map<String, SubsetTag> tags = new HashMap<>();
private static final SubsetTag root = new SubsetTag("");
protected static boolean enableSearchBySubsets = false;
@@ -438,6 +444,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
updateHiddenItems();
SearchField.searchParser.clearCache();
}
}
@@ -449,24 +456,19 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
parentpath -> parentpath.equals(path.toLowerCase())
|| parentpath.startsWith(path.toLowerCase() + "."));
updateHiddenItems();
SearchField.searchParser.clearCache();
}
}
public static boolean isHidden(ItemStack item) {
try {
if (hiddenReadLock.tryLock(5, TimeUnit.SECONDS)) {
try {
return hiddenItems.contains(item);
} finally {
hiddenReadLock.unlock();
}
} else {
NEIClientConfig.logger.error("Unable to obtain read lock in 'isHidden'");
}
} catch (InterruptedException e) {
e.printStackTrace();
final String guid = StackInfo.getItemStackGUID(item);
synchronized (hiddenLock) {
return hiddenItems.contains(guid);
}
return false;
}
private static List<String> toGUIDs(List<ItemStack> items) {
return items.stream().map(StackInfo::getItemStackGUID).collect(Collectors.toList());
}
private static List<ItemStack> getItems(SubsetTag tag, List<ItemStack> items) {
@@ -480,90 +482,50 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
public static void showOnly(SubsetTag tag) {
try {
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
try {
hiddenItems.clear();
hiddenItems.addAll(getItems(root, new ArrayList<>()));
hiddenItems.removeAll(getItems(tag, new ArrayList<>()));
} finally {
hiddenWriteLock.unlock();
}
} else {
NEIClientConfig.logger.error("Unable to obtain write lock in 'showOnly'");
}
final List<String> allItems = toGUIDs(getItems(root, new ArrayList<>()));
final List<String> tagItems = toGUIDs(getItems(tag, new ArrayList<>()));
calculateVisibility();
} catch (InterruptedException e) {
e.printStackTrace();
synchronized (hiddenLock) {
hiddenItems.clear();
hiddenItems.addAll(allItems);
hiddenItems.removeAll(tagItems);
}
calculateVisibility();
}
public static void setHidden(SubsetTag tag, boolean hidden) {
try {
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
try {
List<ItemStack> tagItems = getItems(tag, new ArrayList<>());
final List<String> tagItems = toGUIDs(getItems(tag, new ArrayList<>()));
if (hidden) {
hiddenItems.addAll(tagItems);
} else {
hiddenItems.removeAll(tagItems);
}
} finally {
hiddenWriteLock.unlock();
}
synchronized (hiddenLock) {
if (hidden) {
hiddenItems.addAll(tagItems);
} else {
NEIClientConfig.logger.error("Unable to obtain write lock in 'setHidden'");
hiddenItems.removeAll(tagItems);
}
calculateVisibility();
} catch (InterruptedException e) {
e.printStackTrace();
}
calculateVisibility();
}
public static void setHidden(ItemStack item, boolean hidden) {
try {
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
try {
final String guid = StackInfo.getItemStackGUID(item);
if (hidden) {
hiddenItems.add(item);
} else {
hiddenItems.remove(item);
}
} finally {
hiddenWriteLock.unlock();
}
synchronized (hiddenLock) {
if (hidden) {
hiddenItems.add(guid);
} else {
NEIClientConfig.logger.error("Unable to obtain write lock in 'setHidden'");
hiddenItems.remove(guid);
}
calculateVisibility();
} catch (InterruptedException e) {
e.printStackTrace();
}
calculateVisibility();
}
public static void unhideAll() {
try {
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
try {
hiddenItems.clear();
} finally {
hiddenWriteLock.unlock();
}
} else {
NEIClientConfig.logger.error("Unable to obtain write lock in 'unhideAll'");
}
calculateVisibility();
} catch (InterruptedException e) {
e.printStackTrace();
synchronized (hiddenLock) {
hiddenItems.clear();
}
calculateVisibility();
}
public static void updateHiddenItems() {
@@ -635,42 +597,43 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
public static void loadHidden() {
final List<ItemStack> itemList = new LinkedList<>();
final Set<String> guids = new HashSet<>();
try {
final NBTTagList guidList = NEIClientConfig.world.nbt.getTagList("hiddenItems", 8);
for (int i = 0; i < guidList.tagCount(); i++) {
guids.add(guidList.getStringTagAt(i));
}
final NBTTagList list = NEIClientConfig.world.nbt.getTagList("hiddenItems", 10);
for (int i = 0; i < list.tagCount(); i++) {
itemList.add(ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i)));
final ItemStack stack = ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i));
if (stack != null) {
guids.add(StackInfo.getItemStackGUID(stack));
}
}
} catch (Exception e) {
NEIClientConfig.logger.error("Error loading hiddenItems", e);
return;
}
try {
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
try {
hiddenItems.clear();
hiddenItems.addAll(itemList);
} finally {
hiddenWriteLock.unlock();
}
} else {
NEIClientConfig.logger.error("Unable to obtain second write lock in 'loadHidden'");
}
} catch (InterruptedException e) {
e.printStackTrace();
synchronized (hiddenLock) {
hiddenItems.clear();
hiddenItems.addAll(guids);
}
}
public static void saveHidden() {
if (NEIClientConfig.world == null) return;
final NBTTagList list = new NBTTagList();
for (ItemStack stack : hiddenItems.values()) {
list.appendTag(stack.writeToNBT(new NBTTagCompound()));
synchronized (hiddenLock) {
for (String guid : hiddenItems) {
list.appendTag(new NBTTagString(guid));
}
}
NEIClientConfig.world.nbt.setTag("hiddenItems", list);
@@ -688,14 +651,22 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
public void execute() {
try {
final List<SubsetTag> list = new ArrayList<>(tags.values());
final List<SubsetTag> list;
synchronized (tags) {
list = new ArrayList<>(tags.values());
}
root.clearCache();
list.parallelStream().forEach(tag -> {
tag.clearCache();
ItemList.items.stream().filter(tag.filter::matches)
.collect(Collectors.toCollection(() -> tag.items));
if (!(tag.filter instanceof NothingItemFilter)) {
ItemList.items.stream().filter(tag.filter::matches)
.collect(Collectors.toCollection(() -> tag.items));
}
});
for (SubsetTag tag : list) {
@@ -719,18 +690,19 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
for (SubsetTag tag : list) {
tag.children.sort(Comparator.comparing(SubsetTag::displayName));
tag.calculatedWidth = tag.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0);
tag.calculatedWidth = -1;
}
root.children.removeIf(child -> child.children.isEmpty() && child.items.isEmpty());
root.children.sort(Comparator.comparing(SubsetTag::displayName));
root.calculatedWidth = root.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0);
root.calculatedWidth = -1;
calculateVisibility(root);
} catch (Throwable e) {
e.printStackTrace();
}
SearchField.searchParser.clearCache();
}
}
@@ -742,15 +714,17 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
@Override
public String getRenderLabel() {
public int getRenderLabelColor() {
return NEIClientConfig.subsetWidgetOnTop() ? 0 : ColorUtils.subsetWidget.getColor();
}
@Override
public String getRenderLabel() {
if (NEIClientConfig.subsetWidgetOnTop()) {
return NEIClientUtils.translate("inventory.item_subsets");
} else {
return EnumChatFormatting.DARK_PURPLE + String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX))
+ EnumChatFormatting.RESET;
return String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX));
}
}
@Override
@@ -788,7 +762,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
root.resize(screen, parent, dropRight);
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
GuiContainerManager.enable2DRender();
GuiContainerManager.drawItems.zLevel += 100;
@@ -939,7 +913,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
}
@Override
public Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> hotkeys) {
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
if (SubsetWidget.hoverStack != null) {
+1 -1
View File
@@ -89,7 +89,7 @@ public abstract class TextField extends Widget {
onTextChange(oldText);
}
return handled;
return handled || keyID == Keyboard.KEY_SPACE;
}
public abstract void onTextChange(String oldText);
+18 -2
View File
@@ -1,6 +1,7 @@
package codechicken.nei;
import java.util.List;
import java.util.Map;
import net.minecraft.item.ItemStack;
@@ -14,8 +15,6 @@ public abstract class Widget {
public void postDraw(int mx, int my) {}
public void postDrawTooltips(int mx, int my, List<String> tooltip) {}
public boolean handleClick(int mx, int my, int button) {
return true;
}
@@ -30,6 +29,10 @@ public abstract class Widget {
public void lastKeyTyped(int keyID, char keyChar) {}
public boolean onHotkeyPressed() {
return false;
}
public boolean handleClickExt(int mx, int my, int button) {
return false;
}
@@ -38,6 +41,10 @@ public abstract class Widget {
return false;
}
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
return hotkeys;
}
public void update() {}
public Rectangle4i bounds() {
@@ -58,6 +65,14 @@ public abstract class Widget {
return tooltip;
}
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> tooltip) {
return tooltip;
}
public boolean isFocused() {
return false;
}
public void loseFocus() {}
public void gainFocus() {}
@@ -67,4 +82,5 @@ public abstract class Widget {
public int z;
public int w;
public int h;
}
@@ -0,0 +1,267 @@
package codechicken.nei;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import net.minecraft.item.ItemStack;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
public class WidgetContainer extends Widget {
protected final List<Widget> widgets = new ArrayList<>();
protected boolean isFocused = false;
public void addWidget(Widget widget) {
this.widgets.add(widget);
}
public void setWidgets(List<Widget> widgets) {
final boolean isFocused = this.isFocused;
clear();
loseFocus();
for (Widget widget : widgets) {
addWidget(widget);
}
this.isFocused = isFocused;
}
public void clear() {
this.widgets.clear();
this.isFocused = false;
}
public List<Widget> getWidgets() {
return Collections.unmodifiableList(this.widgets);
}
public boolean removeIf(Predicate<Widget> filter) {
return this.widgets.removeIf(filter);
}
@Override
public void draw(int mousex, int mousey) {
drawWidgets(mousex, mousey);
}
protected void drawWidgets(int mousex, int mousey) {
final Rectangle4i bounds = bounds();
for (Widget widget : this.widgets) {
if (widget.bounds().intersects(bounds)) {
widget.draw(mousex, mousey);
}
}
}
@Override
public void update() {
for (Widget widget : this.widgets) {
widget.update();
}
Collections.sort(this.widgets, Comparator.comparingInt((Widget widget) -> widget.z));
}
public Widget getWidgetUnderMouse(int mousex, int mousey) {
if (!bounds().contains(mousex, mousey)) {
return null;
}
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (widget.contains(mousex, mousey)) {
return widget;
}
}
return null;
}
@Override
public boolean onMouseWheel(int scroll, int mousex, int mousey) {
final Widget widget = getWidgetUnderMouse(mousex, mousey);
return widget != null && widget.onMouseWheel(scroll, mousex, mousey);
}
@Override
public boolean handleClick(int mousex, int mousey, int button) {
if (contains(mousex, mousey)) {
gainFocus();
}
final Widget widget = getWidgetUnderMouse(mousex, mousey);
return widget != null && widget.handleClick(mousex, mousey, button);
}
@Override
public boolean handleClickExt(int mousex, int mousey, int button) {
loseFocus();
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (!widget.contains(mousex, mousey) && widget.handleClickExt(mousex, mousey, button)) {
return true;
}
}
return false;
}
@Override
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
widget.mouseDragged(mousex, mousey, button, heldTime);
}
}
@Override
public void mouseUp(int mousex, int mousey, int button) {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
widget.mouseUp(mousex, mousey, button);
}
}
@Override
public boolean handleKeyPress(int keyCode, char keyChar) {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (widget.handleKeyPress(keyCode, keyChar)) {
return true;
}
}
return false;
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
widget.lastKeyTyped(keyID, keyChar);
}
}
@Override
public boolean onHotkeyPressed() {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (widget.onHotkeyPressed()) {
return true;
}
}
return false;
}
@Override
public boolean isFocused() {
return this.isFocused;
}
@Override
public void loseFocus() {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (widget.isFocused()) {
widget.loseFocus();
}
}
this.isFocused = false;
}
@Override
public void gainFocus() {
final Point mouse = GuiDraw.getMousePosition();
final Widget widget = getWidgetUnderMouse(mouse.x, mouse.y);
if (widget != null) {
if (!widget.isFocused()) {
loseFocus();
widget.gainFocus();
}
} else {
loseFocus();
}
this.isFocused = true;
}
@Override
public ItemStack getStackMouseOver(int mousex, int mousey) {
final Widget widget = getWidgetUnderMouse(mousex, mousey);
if (widget != null) {
return widget.getStackMouseOver(mousex, mousey);
}
return null;
}
@Override
public void postDraw(int mousex, int mousey) {
for (Widget widget : this.widgets) {
widget.postDraw(mousex, mousey);
}
}
@Override
public void onGuiClick(int mousex, int mousey) {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
widget.onGuiClick(mousex, mousey);
}
}
@Override
public List<String> handleTooltip(int mousex, int mousey, List<String> tooltip) {
final Widget widget = getWidgetUnderMouse(mousex, mousey);
if (widget != null) {
return widget.handleTooltip(mousex, mousey, tooltip);
}
return tooltip;
}
@Override
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
final Widget widget = getWidgetUnderMouse(mousex, mousey);
if (widget != null) {
return widget.handleHotkeys(mousex, mousey, hotkeys);
}
return hotkeys;
}
}
@@ -1,5 +1,11 @@
package codechicken.nei;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import net.minecraft.block.Block;
import net.minecraft.block.BlockSnow;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
@@ -15,6 +21,8 @@ import net.minecraft.world.chunk.Chunk;
import org.lwjgl.opengl.GL11;
import com.cardinalstar.cubicchunks.world.api.IMinMaxHeight;
import codechicken.lib.math.MathHelper;
import codechicken.nei.KeyManager.IKeyStateTracker;
import cpw.mods.fml.relauncher.ReflectionHelper;
@@ -26,20 +34,23 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
public static int mobOverlay = 0;
private static byte[] mobSpawnCache;
private static long mobOverlayUpdateTime;
private static String oregenPatternName;
public static void reset() {
mobOverlay = 0;
chunkOverlay = 0;
oregenPatternName = null;
}
@Override
public void tickKeyStates() {
if (Minecraft.getMinecraft().currentScreen != null) return;
if (KeyManager.keyStates.get("world.moboverlay").down) mobOverlay = (mobOverlay + 1) % 2;
if (KeyManager.keyStates.get("world.chunkoverlay").down) {
if (KeyManager.isPressed("world.moboverlay")) {
mobOverlay = (mobOverlay + 1) % 2;
NEIClientUtils
.printChatMessage(new ChatComponentText(NEIClientUtils.translate("chat.moboverlay." + mobOverlay)));
}
if (KeyManager.isPressed("world.chunkoverlay")) {
chunkOverlay = (chunkOverlay + 1) % (NEIModContainer.isGT5Loaded() ? 4 : 3);
// 0-2 (or 3 with gt)
NEIClientUtils.printChatMessage(
@@ -49,6 +60,7 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
}
public static void render(float frame) {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_LINE_BIT);
GL11.glPushMatrix();
Entity entity = Minecraft.getMinecraft().renderViewEntity;
@@ -62,9 +74,12 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
GL11.glTranslated(-interpPosX + intOffsetX, -interpPosY + intOffsetY, -interpPosZ + intOffsetZ);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDepthFunc(GL11.GL_LEQUAL);
renderChunkBounds(entity, intOffsetX, intOffsetY, intOffsetZ);
renderMobSpawnOverlay(entity, intOffsetX, intOffsetY, intOffsetZ);
GL11.glPopMatrix();
GL11.glPopAttrib();
}
private static void buildMobSpawnOverlay(Entity entity) {
@@ -73,9 +88,11 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
}
World world = entity.worldObj;
int worldHeight = world.getHeight();
int x1 = (int) entity.posX;
int z1 = (int) entity.posZ;
int y1 = (int) MathHelper.clip(entity.posY, 16, world.getHeight() - 16);
int y1 = (int) MathHelper.clip(entity.posY, 16, worldHeight - 16);
for (int i = 0; i <= 32; i++) {
int x = x1 - 16 + i;
@@ -94,6 +111,12 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
for (int k = 0; k <= 32; k++) {
int y = y1 - 16 + k;
if (y >= worldHeight) {
mobSpawnCache[bufIndex + k] = -1;
continue;
}
if (y > maxHeight) {
mobSpawnCache[bufIndex + k] = -1;
break;
@@ -104,6 +127,15 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
}
}
private static double getOverlayYOffset(World world, int x, int y, int z) {
Block block = world.getBlock(x, y, z);
if (block instanceof BlockSnow) {
int meta = world.getBlockMetadata(x, y, z) & 7;
return (1 + meta) / 8.0 + 0.02;
}
return 0.02;
}
private static void renderMobSpawnOverlay(Entity entity, int intOffsetX, int intOffsetY, int intOffsetZ) {
if (mobOverlay == 0) {
if (mobSpawnCache != null) {
@@ -159,10 +191,12 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
curSpawnMode = spawnMode;
}
tess.addVertex(x, y + 0.02, z);
tess.addVertex(x + 1, y + 0.02, z + 1);
tess.addVertex(x + 1, y + 0.02, z);
tess.addVertex(x, y + 0.02, z + 1);
double yOff = getOverlayYOffset(world, x + intOffsetX, y + intOffsetY, z + intOffsetZ);
tess.addVertex(x, y + yOff, z);
tess.addVertex(x + 1, y + yOff, z + 1);
tess.addVertex(x + 1, y + yOff, z);
tess.addVertex(x, y + yOff, z + 1);
}
}
}
@@ -177,6 +211,10 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
private static final AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(0, 0, 0, 0, 0, 0);
private static byte getSpawnMode(Chunk chunk, int x, int y, int z) {
if (y >= chunk.worldObj.getHeight()) {
return 0;
}
if (chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15) >= 8
|| !SpawnerAnimals.canCreatureTypeSpawnAtLocation(EnumCreatureType.monster, chunk.worldObj, x, y, z)) {
return 0;
@@ -199,6 +237,9 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
private static void renderChunkBounds(Entity entity, int intOffsetX, int intOffsetY, int intOffsetZ) {
if (chunkOverlay == 0) return;
int worldMinY = worldMinHeight(entity.worldObj);
int worldMaxY = worldMaxHeight(entity.worldObj);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
@@ -217,14 +258,13 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
double dy = 128;
double y1 = Math.floor(entity.posY - dy / 2);
double y2 = y1 + dy;
if (y1 < 0) {
y1 = 0;
y2 = dy;
if (y1 < worldMinY) {
y1 = worldMinY;
y2 = worldMinY + dy;
}
if (y1 > entity.worldObj.getHeight()) {
y2 = entity.worldObj.getHeight();
y1 = y2 - dy;
if (y2 > worldMaxY) {
y2 = worldMaxY;
y1 = Math.max(worldMaxY - dy, worldMinY);
}
y1 -= intOffsetY;
@@ -250,23 +290,44 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
tess.addVertex(x1, y2, z1);
}
if (NEIModContainer.isCCLoaded()) {
tess.setColorRGBA_F(0, 0.5F, 0.9F, (float) dist * 0.5F);
int cubeBase = (int) Math.floor((y1 + intOffsetY) / 16.0) * 16;
for (int cubeY = cubeBase; cubeY <= y2 + intOffsetY; cubeY += 16) {
double ry = cubeY - intOffsetY;
tess.addVertex(x1, ry, z1);
tess.addVertex(x2, ry, z1);
tess.addVertex(x1, ry, z2);
tess.addVertex(x2, ry, z2);
tess.addVertex(x1, ry, z1);
tess.addVertex(x1, ry, z2);
tess.addVertex(x2, ry, z1);
tess.addVertex(x2, ry, z2);
}
}
if (cx == 0 && cz == 0) {
if (chunkOverlay == 2) {
dy = 32;
y1 = Math.floor(entity.posY - dy / 2);
y2 = y1 + dy;
if (y1 < 0) {
y1 = 0;
y2 = dy;
if (y1 < worldMinY) {
y1 = worldMinY;
y2 = worldMinY + dy;
}
if (y1 > entity.worldObj.getHeight()) {
y2 = entity.worldObj.getHeight();
y1 = y2 - dy;
if (y2 > worldMaxY) {
y2 = worldMaxY;
y1 = Math.max(worldMaxY - dy, worldMinY);
}
y1 -= intOffsetY;
y2 -= intOffsetY;
double centerX = (x1 + x2) / 2;
double centerZ = (z1 + z2) / 2;
tess.setColorRGBA_F(0.9F, 0.9F, 0, (float) dist);
tess.addVertex(centerX, y1, centerZ);
tess.addVertex(centerX, y2, centerZ);
tess.setColorRGBA_F(0, 0.9F, 0, 0.4F);
for (double y = (int) y1; y <= y2; y++) {
@@ -361,20 +422,55 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
GL11.glDisable(GL11.GL_BLEND);
}
private static String getOregenPatternName() {
if (oregenPatternName == null) {
try {
final ClassLoader loader = WorldOverlayRenderer.class.getClassLoader();
final Class<?> gtWorldGenerator = ReflectionHelper
.getClass(loader, "gregtech.common.GTWorldgenerator", "gregtech.common.GT_Worldgenerator");
oregenPatternName = ((Enum<?>) gtWorldGenerator.getDeclaredField("oregenPattern").get(null)).name();
} catch (Exception ignored) {
oregenPatternName = "AXISSYMMETRICAL";
}
private static int worldMinHeight(World world) {
if (NEIModContainer.isCCLoaded() && world instanceof IMinMaxHeight worldHeights) {
return worldHeights.getMinHeight();
} else {
return 0;
}
}
return oregenPatternName;
private static int worldMaxHeight(World world) {
if (NEIModContainer.isCCLoaded() && world instanceof IMinMaxHeight worldHeights) {
return worldHeights.getMaxHeight();
} else {
return world.getHeight();
}
}
private static Callable<String> oregenPatternReader;
private static String getOregenPatternName() {
if (oregenPatternReader == null) {
oregenPatternReader = resolveOregenPatternReader();
}
try {
return oregenPatternReader.call();
} catch (Exception ignored) {
return "AXISSYMMETRICAL";
}
}
private static Callable<String> resolveOregenPatternReader() {
try {
final Class<?> gtWorldGenerator = ReflectionHelper.getClass(
WorldOverlayRenderer.class.getClassLoader(),
"gregtech.common.GTWorldgenerator",
"gregtech.common.GT_Worldgenerator");
try {
// GT5-Unofficial >= 5.09.54.29 support
final Method accessor = gtWorldGenerator.getMethod("getOregenPattern");
return () -> ((Enum<?>) accessor.invoke(null)).name();
} catch (NoSuchMethodException legacy) {
// Older GT
final Field oregenPattern = gtWorldGenerator.getDeclaredField("oregenPattern");
oregenPattern.setAccessible(true);
return () -> ((Enum<?>) oregenPattern.get(null)).name();
}
} catch (Exception ignored) {
return () -> "AXISSYMMETRICAL";
}
}
public static void load() {
+36 -15
View File
@@ -1,15 +1,18 @@
package codechicken.nei.api;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import net.minecraft.block.Block;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.Fluid;
import org.lwjgl.input.Keyboard;
@@ -20,7 +23,6 @@ import codechicken.nei.ItemList;
import codechicken.nei.ItemSorter;
import codechicken.nei.ItemStackSet;
import codechicken.nei.KeyManager;
import codechicken.nei.KeyManager.KeyState;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.OffsetPositioner;
@@ -32,7 +34,7 @@ import codechicken.nei.SubsetWidget.SubsetTag;
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
import codechicken.nei.config.Option;
import codechicken.nei.config.OptionKeyBind;
import codechicken.nei.item.FluidDrawer;
import codechicken.nei.recipe.CatalystInfo;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
@@ -146,14 +148,14 @@ public class API {
* Hide an item from the item panel Damage values of OreDictionary.WILDCARD_VALUE and ItemStackMap.WILDCARD_TAG tags
* function as wildcards for their respective variables
*/
public static void hideItem(ItemStack item) {
public static synchronized void hideItem(ItemStack item) {
if (!ItemInfo.hiddenItems.contains(item)) {
ItemInfo.hiddenItems.add(item);
LayoutManager.markItemsDirty();
}
}
public static void hideItem(String rule) {
public static synchronized void hideItem(String rule) {
ItemFilter filter = ItemStackFilterParser.parse(rule);
if (filter != null) {
ItemInfo.hiddenItemsRules.filters.add(filter);
@@ -176,6 +178,23 @@ public class API {
}
}
/**
* Add an alias name that this item can be found by in the search field, in addition to its display name.
*
* @param aliases translation keys, localized at search time
*/
public static void setAliases(ItemStack item, String... aliases) {
final List<String> list = Arrays.asList(aliases).stream().filter(Objects::nonNull).map(String::trim)
.filter(s -> !s.isEmpty()).collect(Collectors.toList());
if (list.isEmpty()) {
ItemInfo.itemAliases.remove(item);
} else {
ItemInfo.itemAliases.put(item, list);
}
}
/**
* Adds an item to the item panel. Any items added using this function will override the default search pattern.
*
@@ -206,20 +225,12 @@ public class API {
* @param defaultKey The default value, commonly obtained from {@link Keyboard}
*/
public static void addKeyBind(String ident, int defaultKey) {
NEIClientConfig.setDefaultKeyBinding(ident, defaultKey);
KeyManager.keyStates.put(ident, new KeyState());
addOption(new OptionKeyBind(ident));
KeyManager.registerKeyBinding(ident, defaultKey);
}
/**
* Add a custom KeyBinding to be configured in the Controls menu.
*
* @param ident An identifier for your key, eg "shoot"
* @param defaultKey The default value, commonly obtained from {@link Keyboard}
*/
@Deprecated
public static void addHashBind(String ident, int defaultKey) {
NEIClientConfig.setDefaultKeyBinding(ident, defaultKey);
addOption(new OptionKeyBind(ident, true));
KeyManager.registerKeyBinding(ident, defaultKey);
}
public static void addOption(Option option) {
@@ -368,6 +379,16 @@ public class API {
StackInfo.stackStringifyHandlers.add(handler);
}
/**
* Replaces the default rendering of a fluid in NEI fluid display slots and fluid tanks.
*
* @param fluid the fluid to render
* @param renderer the renderer, or null to restore the default rendering
*/
public static void registerFluidRenderer(Fluid fluid, IFluidRenderer renderer) {
FluidDrawer.registerFluidRenderer(fluid, renderer);
}
/**
* Adds an association between an ingredient and what it can craft. (i.e. Furnace ItemStack -> Smelting and Fuel
* Recipes) Allows players to see what ingredient they need to craft in order to make recipes from a recipe
@@ -24,7 +24,7 @@ public class DefaultOverlayRenderer implements IRecipeOverlayRenderer {
@Override
public void renderOverlay(GuiContainerManager gui, Slot slot) {
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(770, 1);
+14 -7
View File
@@ -16,7 +16,6 @@ import codechicken.nei.NEIDummySlotHandler;
import codechicken.nei.NEIPotionGuiHandler;
import codechicken.nei.recipe.CheatItemHandler;
import codechicken.nei.recipe.FillFluidContainerHandler;
import codechicken.nei.recipe.GuiRecipeCatalyst;
import codechicken.nei.recipe.SearchInputDropHandler;
public class GuiInfo {
@@ -31,7 +30,6 @@ public class GuiInfo {
API.registerNEIGuiHandler(new NEICreativeGuiHandler());
API.registerNEIGuiHandler(new NEIChestGuiHandler());
API.registerNEIGuiHandler(new NEIDummySlotHandler());
API.registerNEIGuiHandler(new GuiRecipeCatalyst(null));
API.registerNEIGuiHandler(new SearchInputDropHandler());
API.registerNEIGuiHandler(new FillFluidContainerHandler());
API.registerNEIGuiHandler(new CheatItemHandler());
@@ -53,20 +51,29 @@ public class GuiInfo {
}
public static boolean hideItemPanelSlot(GuiContainer gui, Rectangle4i rect) {
return hideItemPanelSlot(gui, rect, getGuiHandlersSnapshot());
}
public static INEIGuiHandler[] getGuiHandlersSnapshot() {
try {
readLock.lock();
if (guiHandlers.stream()
.anyMatch(handler -> handler.hideItemPanelSlot(gui, rect.x, rect.y, rect.w, rect.h))) {
return true;
}
return guiHandlers.toArray(new INEIGuiHandler[0]);
} finally {
readLock.unlock();
}
}
public static boolean hideItemPanelSlot(GuiContainer gui, Rectangle4i rect, INEIGuiHandler[] handlers) {
for (INEIGuiHandler handler : handlers) {
if (handler.hideItemPanelSlot(gui, rect.x, rect.y, rect.w, rect.h)) return true;
}
for (Object item : gui.buttonList) {
GuiButton button = (GuiButton) item;
if ((new Rectangle4i(button.xPosition, button.yPosition, button.width, button.height)).intersects(rect)) {
if (button.xPosition + button.width > rect.x && button.xPosition < rect.x + rect.w
&& button.yPosition + button.height > rect.y
&& button.yPosition < rect.y + rect.h) {
return true;
}
}
@@ -15,7 +15,7 @@ public interface IBookmarkContainerHandler {
for (int i = 0; i < slots.size() - 4 * 9; i++) {
if (slots.get(i).getHasStack()) {
stacks.add(slots.get(i).getStack());
stacks.add(slots.get(i).getStack().copy());
}
}
@@ -0,0 +1,17 @@
package codechicken.nei.api;
import net.minecraftforge.fluids.Fluid;
/**
* Custom renderer for a fluid, used by NEI fluid display slots and fluid tanks in recipes.
* <p>
* Register with {@link API#registerFluidRenderer(Fluid, IFluidRenderer)}.
*/
public interface IFluidRenderer {
/**
* Renders one 16x16 tile of the fluid. Partial tiles of a tank are clipped by NEI. The amount overlay is drawn by
* NEI.
*/
void renderFluid(Fluid fluid, int x, int y);
}
@@ -1,15 +1,9 @@
package codechicken.nei.api;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.VisiblityData;
/**
* Lets you just override those things you want to
*/
@@ -17,31 +11,6 @@ public class INEIGuiAdapter implements INEIGuiHandler {
static final Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\].+*?^$\\\\|]");
@Override
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
return currentVisibility;
}
@Override
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
return Collections.emptyList();
}
@Override
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
return null;
}
@Override
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
return false;
}
@Override
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
return false;
}
protected String formattingText(String displayName) {
return SPECIAL_REGEX_CHARS.matcher(EnumChatFormatting.getTextWithoutFormattingCodes(displayName))
.replaceAll("\\\\$0");
@@ -1,5 +1,6 @@
package codechicken.nei.api;
import java.util.Collections;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
@@ -12,23 +13,29 @@ import codechicken.nei.VisiblityData;
*/
public interface INEIGuiHandler {
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility);
default VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
return currentVisibility;
}
/**
* NEI will give the specified item to the InventoryRange returned if the player's inventory is full. Should not
* return null, just an empty list
*/
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item);
default Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
return Collections.emptyList();
}
/**
* @return A list of TaggedInventoryAreas that will be used with the savestates.
*/
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui);
default List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
return null;
}
/**
* Handles clicks while an itemstack has been dragged from the item panel. Use this to set configurable slots and
* the like. Changes made to the stackSize of the dragged stack will be kept
*
*
* @param gui The current gui instance
* @param mousex The x position of the mouse
* @param mousey The y position of the mouse
@@ -37,18 +44,22 @@ public interface INEIGuiHandler {
* @return True if the drag n drop was handled. False to resume processing through other routes. The held stack will
* be deleted if draggedStack.stackSize == 0
*/
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button);
default boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
return false;
}
/**
* Used to prevent the item panel from drawing on top of other gui elements. This function will also be called with
* a 1x1 size rectangle on the mouse position for determining if the given coordinate should override item panel
* functions such as scrolling
*
*
* @param x The x coordinate of the rectangle bounding the slot
* @param y The y coordinate of the rectangle bounding the slot
* @param w The w coordinate of the rectangle bounding the slot
* @param h The h coordinate of the rectangle bounding the slot
* @return true if the item panel slot within the specified rectangle should not be rendered.
*/
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h);
default boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
return false;
}
}
@@ -1,14 +1,12 @@
package codechicken.nei.api;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import codechicken.nei.PositionedStack;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.recipe.GuiOverlayButton.ItemOverlayState;
import codechicken.nei.recipe.IRecipeHandler;
@@ -32,29 +30,19 @@ public interface IOverlayHandler {
return false;
}
default boolean requireShiftForOverlayRecipe() {
return NEIClientConfig.requireShiftForOverlayRecipe();
}
default List<ItemOverlayState> presenceOverlay(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex) {
final List<ItemOverlayState> itemPresenceSlots = new ArrayList<>();
final List<PositionedStack> ingredients = recipe.getIngredientStacks(recipeIndex);
final List<ItemStack> invStacks = firstGui.inventorySlots.inventorySlots.stream()
.filter(
s -> s != null && s.getStack() != null
&& s.getStack().stackSize > 0
&& s.isItemValid(s.getStack())
&& s.canTakeStack(firstGui.mc.thePlayer))
.map(s -> s.getStack().copy()).collect(Collectors.toList());
for (PositionedStack stack : ingredients) {
Optional<ItemStack> used = invStacks.stream().filter(is -> is.stackSize > 0 && stack.contains(is))
.findAny();
itemPresenceSlots.add(new ItemOverlayState(stack, used.isPresent()));
if (used.isPresent()) {
ItemStack is = used.get();
is.stackSize -= 1;
}
}
return itemPresenceSlots;
return NEIClientUtils.presenceOverlay(
recipe.getIngredientStacks(recipeIndex),
firstGui.inventorySlots.inventorySlots.stream()
.filter(
s -> s != null && s.getStack() != null
&& s.getStack().stackSize > 0
&& s.isItemValid(s.getStack())
&& s.canTakeStack(firstGui.mc.thePlayer))
.map(s -> s.getStack().copy()).collect(Collectors.toList()));
}
}
@@ -1,8 +1,5 @@
package codechicken.nei.api;
import java.util.List;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.IRecipeHandler;
public interface IRecipeFilter {
@@ -12,7 +9,6 @@ public interface IRecipeFilter {
public IRecipeFilter getRecipeFilter();
}
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
List<PositionedStack> others);
public boolean matches(IRecipeHandler handler, int recipeIndex);
}
@@ -18,6 +18,14 @@ public interface IStackStringifyHandler {
return null;
}
default ItemStack normalizeRecipeQueryStack(ItemStack stack) {
return null;
}
default void pauseItemDamageSound(boolean pause) {}
default boolean isFluidDisplayItem(ItemStack stack) {
return false;
}
}
+49 -16
View File
@@ -1,6 +1,7 @@
package codechicken.nei.api;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -35,6 +36,7 @@ import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.RegistryNamespaced;
import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.BiomeDictionary;
import net.minecraftforge.common.IShearable;
import net.minecraftforge.oredict.OreDictionary;
@@ -44,6 +46,8 @@ import com.google.common.collect.ArrayListMultimap;
import codechicken.core.featurehack.GameDataManipulator;
import codechicken.nei.InfiniteStackSizeHandler;
import codechicken.nei.InfiniteToolHandler;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.PatternItemFilter;
import codechicken.nei.ItemMobSpawner;
import codechicken.nei.ItemStackMap;
import codechicken.nei.ItemStackSet;
@@ -56,15 +60,16 @@ import codechicken.nei.config.ArrayDumper;
import codechicken.nei.config.HandlerDumper;
import codechicken.nei.config.ItemPanelDumper;
import codechicken.nei.config.RegistryDumper;
import codechicken.nei.filter.AnyMultiItemFilter;
import codechicken.nei.filter.PatternItemFilter;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.item.FluidDisplayRenderer;
import codechicken.nei.item.ItemFluidDisplay;
import codechicken.nei.recipe.BrewingRecipeHandler;
import codechicken.nei.recipe.RecipeItemInputHandler;
import codechicken.nei.search.IdentifierFilter;
import codechicken.nei.search.ModNameFilter;
import codechicken.nei.search.OreDictionaryFilter;
import codechicken.nei.search.TooltipFilter;
import codechicken.nei.util.ItemUntranslator;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.registry.GameRegistry;
@@ -75,14 +80,18 @@ import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier;
*/
public class ItemInfo {
public static enum Layout {
public enum Layout {
HEADER,
BODY,
FOOTER
FOOTER;
public static final Layout[] VALUES = values();
}
public static final ArrayListMultimap<Layout, IHighlightHandler> highlightHandlers = ArrayListMultimap.create();
public static final ItemStackMap<String> nameOverrides = new ItemStackMap<>();
public static final ItemStackMap<List<String>> itemAliases = new ItemStackMap<>();
public static final ItemStackSet hiddenItems = new ItemStackSet();
public static final AnyMultiItemFilter hiddenItemsRules = new AnyMultiItemFilter();
public static final ItemStackSet finiteItems = new ItemStackSet();
@@ -108,6 +117,11 @@ public class ItemInfo {
return nameOverrides.get(stack);
}
public static List<String> getAliases(ItemStack stack) {
final List<String> aliases = itemAliases.get(stack);
return aliases != null ? aliases : Collections.emptyList();
}
public static boolean canBeInfinite(ItemStack stack) {
return !finiteItems.contains(stack);
}
@@ -122,6 +136,7 @@ public class ItemInfo {
public static void preInit() {
addMobSpawnerItem();
setupFluidDisplayItemClient();
}
public static void load() {
@@ -137,6 +152,10 @@ public class ItemInfo {
addSearchProviders();
RecipeItemInputHandler.load();
PresetsList.load();
if (NEIClientConfig.enableItemUntranslator()) {
ItemUntranslator.getInstance().load();
}
}
private static void addSearchProviders() {
@@ -303,6 +322,11 @@ public class ItemInfo {
GameDataManipulator.replaceItem(Block.getIdFromBlock(Blocks.mob_spawner), new ItemMobSpawner());
}
private static void setupFluidDisplayItemClient() {
ItemFluidDisplay.INSTANCE.setCreativeTab(ItemFluidDisplay.tabFluids);
MinecraftForgeClient.registerItemRenderer(ItemFluidDisplay.INSTANCE, new FluidDisplayRenderer());
}
private static void addInfiniteHandlers() {
API.addInfiniteItemHandler(new InfiniteStackSizeHandler());
API.addInfiniteItemHandler(new InfiniteToolHandler());
@@ -387,6 +411,7 @@ public class ItemInfo {
item -> item.getItem() instanceof ItemArmor armor && armor.armorType == 2);
API.addSubset("Items.Armor.Boots", item -> item.getItem() instanceof ItemArmor armor && armor.armorType == 3);
API.addSubset("Items.Food", item -> item.getItem() instanceof ItemFood);
API.addSubset("Items.Fluids", item -> item.getItem() instanceof ItemFluidDisplay);
API.addSubset("Items.Potions.Ingredients", BrewingRecipeHandler.ingredients);
for (CreativeTabs tab : CreativeTabs.creativeTabArray) {
@@ -416,30 +441,38 @@ public class ItemInfo {
Block mouseoverBlock = world.getBlock(x, y, z);
ArrayList<ItemStack> items = new ArrayList<>();
ArrayList<IHighlightHandler> handlers = new ArrayList<>();
if (highlightIdentifiers.containsKey(null)) handlers.addAll(highlightIdentifiers.get(null));
if (highlightIdentifiers.containsKey(mouseoverBlock)) handlers.addAll(highlightIdentifiers.get(mouseoverBlock));
for (IHighlightHandler ident : handlers) {
ItemStack item = ident.identifyHighlight(world, player, hit);
final ItemStack item = ident.identifyHighlight(world, player, hit);
if (item != null) items.add(item);
}
if (items.size() > 0) return items;
if (!items.isEmpty()) return items;
ItemStack pick = mouseoverBlock.getPickBlock(hit, world, x, y, z, player);
if (pick != null) items.add(pick);
try {
final ItemStack pick = mouseoverBlock.getPickBlock(hit, world, x, y, z, player);
if (pick != null) items.add(pick);
} catch (Exception ignored) {}
if (!items.isEmpty()) return items;
if (mouseoverBlock instanceof IShearable shearable) {
if (shearable.isShearable(new ItemStack(Items.shears), world, x, y, z)) {
items.addAll(shearable.onSheared(new ItemStack(Items.shears), world, x, y, z, 0));
}
}
if (!items.isEmpty()) return items;
try {
items.addAll(mouseoverBlock.getDrops(world, x, y, z, world.getBlockMetadata(x, y, z), 0));
} catch (Exception ignored) {}
if (mouseoverBlock instanceof IShearable) {
IShearable shearable = (IShearable) mouseoverBlock;
if (shearable.isShearable(new ItemStack(Items.shears), world, x, y, z))
items.addAll(shearable.onSheared(new ItemStack(Items.shears), world, x, y, z, 0));
}
if (items.size() == 0) items.add(0, new ItemStack(mouseoverBlock, 1, world.getBlockMetadata(x, y, z)));
if (items.isEmpty()) items.add(new ItemStack(mouseoverBlock, 1, world.getBlockMetadata(x, y, z)));
return items;
}
@@ -452,7 +485,7 @@ public class ItemInfo {
MovingObjectPosition mop) {
List<String> retString = new ArrayList<>();
for (ItemInfo.Layout layout : ItemInfo.Layout.values())
for (ItemInfo.Layout layout : Layout.VALUES)
for (IHighlightHandler handler : ItemInfo.highlightHandlers.get(layout))
retString = handler.handleTextData(itemStack, world, player, mop, retString, layout);
@@ -5,6 +5,7 @@ import java.util.LinkedList;
import net.minecraft.world.World;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.config.GuiOptionList;
import codechicken.nei.config.OptionCycled;
public class NEIInfo {
@@ -13,7 +14,9 @@ public class NEIInfo {
public static void load(World world) {
OptionCycled modeOption = (OptionCycled) NEIClientConfig.getOptionList().getOption("inventory.cheatmode");
modeOption.parent.synthesizeEnvironment(false);
final boolean worldConfig = NEIClientConfig.isWorldSpecific("inventory.cheatmode");
// kept alive so the WeakReference in Option.slotRef isn't GC'd before copyGlobals/cycle use it
GuiOptionList env = modeOption.parent.synthesizeEnvironment(worldConfig);
if (!modeOption.optionValid(modeOption.value())) {
modeOption.copyGlobals();
modeOption.cycle();
@@ -19,20 +19,25 @@ import codechicken.nei.BookmarkContainerInfo;
import codechicken.nei.FavoriteRecipes;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemQuantityField;
import codechicken.nei.ItemsGrid.ItemsGridSlot;
import codechicken.nei.KeyManager;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.SearchField;
import codechicken.nei.bookmark.BookmarkGrid;
import codechicken.nei.bookmark.BookmarkGroup;
import codechicken.nei.bookmark.BookmarkItem;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.BookmarkPayload;
import codechicken.nei.bookmark.BookmarksGridSlot;
import codechicken.nei.bookmark.tree.GuiCraftingTree;
import codechicken.nei.recipe.AutoCraftingManager;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiFavoriteButton;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
import codechicken.nei.recipe.HoveredRecipe;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.RecipeHandlerRef;
@@ -42,14 +47,38 @@ import codechicken.nei.util.NEIMouseUtils;
public abstract class ShortcutInputHandler {
private static class HotkeysCache {
public int mousex;
public int mousey;
public ItemStack stack;
public int groupId;
public Map<String, String> hotkeys = new HashMap<>();
public boolean matches(int mousex, int mousey, ItemStack stack, int groupId) {
return Math.abs(this.mousex - mousex) < 16 && Math.abs(this.mousey - mousey) < 16
&& (this.stack == null ? stack == null && this.groupId == groupId
: stack != null && NEIClientUtils.areStacksSameTypeWithNBT(stack, this.stack));
}
public void update(int mousex, int mousey, ItemStack stack, int groupId, Map<String, String> hotkeys) {
this.mousex = mousex;
this.mousey = mousey;
this.stack = stack != null ? stack.copy() : null;
this.groupId = groupId;
this.hotkeys = hotkeys;
}
}
private static HotkeysCache hotkeysCache = new HotkeysCache();
public static boolean handleKeyEvent(ItemStack stackover) {
if (!NEIClientConfig.isLoaded()) {
return false;
}
if (NEIClientConfig.isKeyHashDown("gui.overlay") && stackover == null
&& LayoutManager.overlayRenderer != null) {
if (KeyManager.isKeyDown("gui.overlay") && stackover == null && LayoutManager.overlayRenderer != null) {
LayoutManager.overlayRenderer = null;
return true;
}
@@ -59,16 +88,27 @@ public abstract class ShortcutInputHandler {
if (groupId != -1) {
if (NEIClientConfig.isKeyHashDown("gui.remove_recipe")) {
if (KeyManager.isHashDown("bookmark.remove_recipe", NEIClientUtils.SHIFT_HASH)) {
ItemPanels.bookmarkPanel.removeGroup(groupId);
return true;
}
if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) {
if (KeyManager.isKeyDown("bookmark.pull_items")) {
return ItemPanels.bookmarkPanel.pullBookmarkItems(groupId, NEIClientUtils.shiftKey());
}
if (NEIClientConfig.autocraftingEnabled() && NEIClientConfig.isKeyHashDown("gui.craft_items")
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey()) {
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(groupId).toNBT());
return true;
}
if (ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId)
&& KeyManager.isKeyDown("bookmark.open_crafting_tree")) {
GuiCraftingTree.openCraftingTreeGui(ItemPanels.bookmarkPanel.getGrid(), groupId);
return true;
}
if (NEIClientConfig.autocraftingEnabled() && KeyManager.isKeyDown("gui.craft_items")
&& NEIClientUtils.shiftKey()
&& ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId)) {
final RecipeChainMath math = ItemPanels.bookmarkPanel.getGrid().createRecipeChainMath(groupId);
@@ -91,43 +131,47 @@ public abstract class ShortcutInputHandler {
stackover = stackover.copy();
if (NEIClientConfig.isKeyHashDown("gui.overlay")) {
if (KeyManager.isKeyDown("gui.overlay")) {
return openOverlayRecipe(stackover);
}
if (NEIClientConfig.isKeyHashDown("gui.copy_name")) {
if (KeyManager.isHashDown("copy.name", NEIClientUtils.CTRL_HASH)) {
return copyItemStackName(stackover);
}
if (NEIClientConfig.isKeyHashDown("gui.copy_oredict")) {
if (KeyManager.isHashDown("copy.identifier", NEIClientUtils.CTRL_HASH)) {
return copyItemStackID(stackover);
}
if (KeyManager.isHashDown("copy.oredict", NEIClientUtils.CTRL_HASH)) {
return copyItemStackOreDictionary(stackover);
}
if (NEIClientConfig.isKeyHashDown("gui.chat_link_item")) {
return sendItemStackChatLink(stackover);
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey()) {
return sendRecipeInChatLink(stackover, NEIClientUtils.shiftKey());
}
if (NEIClientConfig.isKeyHashDown("gui.recipe")) {
if (KeyManager.isKeyDown("recipe.recipe")) {
return GuiCraftingRecipe.openRecipeGui("item", stackover);
}
if (NEIClientConfig.isKeyHashDown("gui.usage")) {
if (KeyManager.isKeyDown("recipe.usage")) {
return GuiUsageRecipe.openRecipeGui("item", stackover);
}
if (NEIClientConfig.isKeyHashDown("gui.favorite")) {
if (KeyManager.isHashDown("bookmark.favorite", NEIClientUtils.SHIFT_HASH)) {
return saveFavoriteTree(stackover);
}
if (NEIClientConfig.isKeyHashDown("gui.bookmark")) {
if (KeyManager.isKeyDown("bookmark.add")) {
return saveRecipeInBookmark(stackover, NEIClientUtils.shiftKey(), NEIClientUtils.controlKey());
}
if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) {
if (KeyManager.isKeyDown("bookmark.pull_items")) {
return pullRecipeItems(stackover, NEIClientUtils.shiftKey());
}
if (NEIClientConfig.autocraftingEnabled() && NEIClientConfig.isKeyHashDown("gui.craft_items")
if (NEIClientConfig.autocraftingEnabled() && KeyManager.isKeyDown("gui.craft_items")
&& NEIClientUtils.shiftKey()) {
return runAutoCrafting(stackover, !NEIClientUtils.controlKey());
}
@@ -145,9 +189,9 @@ public abstract class ShortcutInputHandler {
final int button = Mouse.getEventButton();
if (button == 0) {
return GuiCraftingRecipe.openRecipeGui("item", stackover);
return GuiCraftingRecipe.openRecipeGui("item", stackover.copy());
} else if (button == 1) {
return GuiUsageRecipe.openRecipeGui("item", stackover);
return GuiUsageRecipe.openRecipeGui("item", stackover.copy());
}
}
@@ -159,10 +203,45 @@ public abstract class ShortcutInputHandler {
return true;
}
private static boolean sendItemStackChatLink(ItemStack stackover) {
if (stackover == null) return false;
private static boolean copyItemStackID(ItemStack stackover) {
if (stackover == null || stackover.getItem() == null) return false;
GuiScreen.setClipboardString(
stackover.getItem().delegate.name()
+ (stackover.getItemDamage() != 0 ? "/" + stackover.getItemDamage() : ""));
return true;
}
NEIClientUtils.sendChatItemLink(stackover); // I wish clients could just send formatted messages
private static boolean sendRecipeInChatLink(ItemStack stackover, boolean saveIngredients) {
final Point mousePos = GuiDraw.getMousePosition();
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousePos.x, mousePos.y);
if (slot != null && saveIngredients && slot.getType() == BookmarkItemType.RESULT) {
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(slot.getBookmarkItem()).toNBT());
return true;
}
final Recipe recipe = getFocusedRecipe(stackover, mousePos.x, mousePos.y, saveIngredients);
if (recipe != null && saveIngredients) {
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(recipe).toNBT());
return true;
}
final RecipeId recipeId;
if (slot != null) {
recipeId = slot.getType() == BookmarkItemType.INGREDIENT ? null : slot.getRecipeId();
} else {
recipeId = recipe != null ? recipe.getRecipeId() : null;
}
final boolean existsRecipe = NEIClientConfig.getBooleanSetting("inventory.bookmarks.bookmarkItemsWithRecipe");
if (ItemPanels.itemPanel.containsWithSubpanels(mousePos.x, mousePos.y)) {
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
}
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(stackover, existsRecipe ? recipeId : null).toNBT());
return true;
}
@@ -197,12 +276,12 @@ public abstract class ShortcutInputHandler {
if (recipe != null) {
math = RecipeChainMath.of(recipe, 1);
}
} else if (slot.getRecipeId() != null && !slot.isIngredient()) {
} else if (slot.getRecipeId() != null && slot.getType() != BookmarkItemType.INGREDIENT) {
if (slot.getGroup().crafting == null) {
final Recipe recipe = Recipe.of(slot.getRecipeId());
if (recipe != null) {
math = RecipeChainMath.of(recipe, slot != null ? slot.getMultiplier() : 1);
math = RecipeChainMath.of(recipe, Math.max(1, slot.getMultiplier()));
}
} else {
math = ItemPanels.bookmarkPanel.getGrid()
@@ -276,11 +355,14 @@ public abstract class ShortcutInputHandler {
private static boolean saveRecipeInBookmark(ItemStack stackover, boolean saveIngredients, boolean saveStackSize) {
final Point mousePos = GuiDraw.getMousePosition();
// If no slot was removed from the bookmark panel on click:
if (!ItemPanels.bookmarkPanel.removeSlot(mousePos.x, mousePos.y, saveIngredients)) {
final Recipe recipe = getFocusedRecipe(stackover, mousePos.x, mousePos.y, saveIngredients);
// --- Case 1: saving a recipe (with ingredients) ---
if (recipe != null && saveIngredients) {
// If the recipe is not already bookmarked, add it
if (!ItemPanels.bookmarkPanel.removeRecipe(recipe.getRecipeId(), BookmarkGrid.DEFAULT_GROUP_ID)) {
final Recipe singleRecipe = Recipe.of(
Arrays.asList(recipe.getResult(stackover)),
@@ -288,27 +370,38 @@ public abstract class ShortcutInputHandler {
recipe.getIngredients());
singleRecipe.setCustomRecipeId(recipe.getRecipeId());
ItemPanels.bookmarkPanel
.addRecipe(singleRecipe, saveStackSize ? 1 : 0, BookmarkGrid.DEFAULT_GROUP_ID);
ItemPanels.bookmarkPanel.addRecipe(
singleRecipe,
// Determine if stack size should be saved
saveStackSize
|| NEIClientConfig.getBooleanSetting("inventory.bookmarks.bookmarkRecipeWithCount")
? 1
: 0,
BookmarkGrid.DEFAULT_GROUP_ID);
}
// --- Case 2: saving an item only ---
} else {
final RecipeId recipeId = recipe != null ? recipe.getRecipeId() : null;
// Determine if the item is associated with a recipe and should be saved with it
final boolean existsRecipe = recipeId != null
&& ItemPanels.bookmarkPanel.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID)
|| NEIClientConfig.getBooleanSetting("inventory.bookmarks.bookmarkItemsWithRecipe");
// If the item was not removed from the bookmarks, add it
if (!ItemPanels.bookmarkPanel
.removeItem(stackover, existsRecipe ? recipeId : null, BookmarkGrid.DEFAULT_GROUP_ID)) {
// Determine whether to preserve the item count
saveStackSize = saveStackSize || saveIngredients
|| ItemPanels.bookmarkPanel.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID);
if (!saveStackSize) {
stackover = StackInfo.withAmount(stackover, 0);
} else if (ItemPanels.itemPanel.contains(mousePos.x, mousePos.y)
|| ItemPanels.itemPanel.historyPanel.contains(mousePos.x, mousePos.y)) {
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
}
} else if (ItemPanels.itemPanel.containsWithSubpanels(mousePos.x, mousePos.y)) {
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
}
ItemPanels.bookmarkPanel
.addItem(stackover, existsRecipe ? recipeId : null, BookmarkGrid.DEFAULT_GROUP_ID);
@@ -322,41 +415,37 @@ public abstract class ShortcutInputHandler {
private static Recipe getFocusedRecipe(ItemStack stackover, int mousex, int mousey, boolean useFavorites) {
final GuiContainer gui = NEIClientUtils.getGuiContainer();
Recipe recipe = null;
if (useFavorites) {
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
}
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
}
if (itemSlot != null) {
final RecipeId recipeId = itemSlot.getRecipeId();
if (recipeId != null && (recipe = Recipe.of(recipeId)) != null) {
recipe.getIngredients().stream().forEach(ingr -> {
final List<ItemStack> permutations = ingr.getPermutations();
for (int index = 0; index < permutations.size(); index++) {
if (FavoriteRecipes.contains(permutations.get(index))) {
ingr.setActiveIndex(index);
break;
}
}
});
}
if (gui instanceof GuiRecipe<?>guiRecipe) {
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
if (focusedRecipe != null) {
return focusedRecipe;
}
}
if (gui instanceof GuiRecipe guiRecipe) {
recipe = guiRecipe.getFocusedRecipe();
if (!useFavorites) {
return null;
}
final HoveredRecipe hovered = HoveredRecipe.of(gui, stackover);
if (hovered == null || hovered.source != HoveredRecipe.Source.ITEM_PANEL) {
return null;
}
final Recipe recipe = Recipe.of(hovered.recipeId);
if (recipe != null) {
recipe.getIngredients().stream().forEach(ingr -> {
final List<ItemStack> permutations = ingr.getPermutations();
for (int index = 0; index < permutations.size(); index++) {
if (FavoriteRecipes.contains(permutations.get(index))) {
ingr.setActiveIndex(index);
break;
}
}
});
}
return recipe;
@@ -380,88 +469,110 @@ public abstract class ShortcutInputHandler {
return ItemPanels.bookmarkPanel.pullBookmarkItems(math, shift);
}
public static Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, ItemStack stack) {
public static Map<String, String> handleHotkeys(int mousex, int mousey, ItemStack stack) {
final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true);
if (hotkeysCache.matches(mousex, mousey, stack, groupId)) {
return hotkeysCache.hotkeys;
}
final GuiContainer gui = NEIClientUtils.getGuiContainer();
final Map<String, String> hotkeys = new HashMap<>();
if (groupId != -1) {
final boolean isCraftingMode = ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId);
hotkeys.put(
NEIClientConfig.getKeyName("gui.remove_recipe"),
KeyManager.getKeyName("bookmark.remove_recipe", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.remove_recipe"));
hotkeys.put(
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.group.chat_link"));
if (isCraftingMode) {
hotkeys.put(
KeyManager.getKeyName("bookmark.open_crafting_tree"),
NEIClientUtils.translate("bookmark.group.open_crafting_tree"));
}
if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark_pull_items"),
KeyManager.getKeyName("bookmark.pull_items"),
NEIClientUtils.translate("bookmark.group.pull_items"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.pull_items", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.pull_items_shift"));
}
if (NEIClientConfig.autocraftingEnabled() && ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId)) {
if (NEIClientConfig.autocraftingEnabled() && isCraftingMode) {
hotkeys.put(
NEIClientConfig
.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.group.craft_missing"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.craft_all"));
}
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
return hotkeys;
}
if (stack == null) {
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
return hotkeys;
}
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey);
final RecipeId recipeId = getHotkeyRecipeId(gui, mousex, mousey, stack, slot);
final HoveredRecipe hovered = HoveredRecipe.of(gui, stack);
final RecipeId recipeId = hovered == null || hovered.source == HoveredRecipe.Source.OTHER
|| hovered.source == HoveredRecipe.Source.BOOKMARK && hovered.favorite ? null : hovered.recipeId;
if (slot != null) {
final BookmarkGroup group = slot.getGroup();
if (group.crafting != null && group.collapsed) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.remove_recipe"));
} else if (slot.getRecipeId() == null) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
NEIClientUtils.translate("bookmark.remove_item"));
} else if (slot.getRecipeId() == null || slot.getType() == BookmarkItemType.ITEM) {
hotkeys.put(KeyManager.getKeyName("bookmark.add"), NEIClientUtils.translate("bookmark.remove_item"));
} else if (group.crafting != null && group.crafting.recipeRelations.entrySet().stream().anyMatch(
entry -> entry.getKey().equals(slot.getRecipeId())
|| entry.getValue().contains(slot.getRecipeId()))) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
} else {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
if (slot.isIngredient()) {
if (slot.getType() == BookmarkItemType.INGREDIENT) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
KeyManager.getKeyName("bookmark.add"),
NEIClientUtils.translate("bookmark.remove_item"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
} else {
final BookmarkItem item = slot.getBookmarkItem();
if (ItemPanels.bookmarkPanel.getGrid().createChainItems(groupId).values().stream()
.noneMatch(
m -> !m.isIngredient && !m.equals(item) && item.equalsRecipe(m))) {
m -> m.type == BookmarkItemType.RESULT && !m.equals(item)
&& item.equalsRecipe(m))) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
KeyManager.getKeyName("bookmark.add"),
NEIClientUtils.translate("bookmark.remove_recipe"));
} else {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark"),
KeyManager.getKeyName("bookmark.add"),
NEIClientUtils.translate("bookmark.remove_item"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.remove_recipe"));
}
@@ -471,57 +582,67 @@ public abstract class ShortcutInputHandler {
if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark_pull_items"),
KeyManager.getKeyName("bookmark.pull_items"),
NEIClientUtils.translate("bookmark.group.pull_items"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.pull_items", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.pull_items_shift"));
}
} else {
hotkeys.put(NEIClientConfig.getKeyName("gui.bookmark"), NEIClientUtils.translate("bookmark.add_item"));
hotkeys.put(KeyManager.getKeyName("bookmark.add"), NEIClientUtils.translate("bookmark.add_item"));
if (StackInfo.getAmount(stack) > 0) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.CTRL_HASH),
KeyManager.getKeyName("bookmark.add", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.add_item_with_count"));
}
if (recipeId != null) {
hotkeys.put(
NEIClientConfig
.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.add_item_with_recipe_and_count"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.add_item_with_recipe"));
}
if ((ItemPanels.itemPanel.contains(mousex, mousey)
|| ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) && FavoriteRecipes.contains(stack)) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.favorite"),
KeyManager.getKeyName("bookmark.favorite", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("recipe.favorite.bookmark_recipe"));
}
}
hotkeys.put(NEIClientConfig.getKeyName("gui.recipe"), NEIClientUtils.translate("itempanel.open_crafting"));
hotkeys.put(NEIClientConfig.getKeyName("gui.usage"), NEIClientUtils.translate("itempanel.open_usage"));
hotkeys.put(KeyManager.getKeyName("recipe.recipe"), NEIClientUtils.translate("itempanel.open_crafting"));
hotkeys.put(KeyManager.getKeyName("recipe.usage"), NEIClientUtils.translate("itempanel.open_usage"));
hotkeys.put(NEIClientConfig.getKeyName("gui.copy_name"), NEIClientUtils.translate("itempanel.copy_name"));
hotkeys.put(NEIClientConfig.getKeyName("gui.copy_oredict"), NEIClientUtils.translate("itempanel.copy_oredict"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.chat_link_item"),
NEIClientUtils.translate("itempanel.chat_link_item"));
KeyManager.getKeyName("copy.name", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("itempanel.copy_name"));
hotkeys.put(
KeyManager.getKeyName("copy.oredict", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("itempanel.copy_oredict"));
hotkeys.put(
KeyManager.getKeyName("copy.identifier", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("itempanel.copy_id"));
hotkeys.put(
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.item.chat_link"));
if (!(gui instanceof GuiRecipe) && NEIClientConfig.canCheatItem(stack)) {
hotkeys.put(
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
KeyManager.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("itempanel.open_crafting"));
hotkeys.put(
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_RMB),
KeyManager.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_RMB),
NEIClientUtils.translate("itempanel.open_usage"));
hotkeys.put(
KeyManager.getKeyName(NEIClientUtils.CTRL_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate("itempanel.infinite_item"));
} else {
hotkeys.put(
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB),
@@ -538,18 +659,18 @@ public abstract class ShortcutInputHandler {
if (handlerRef.canUseOverlayRenderer(gui)) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.overlay"),
KeyManager.getKeyName("gui.overlay"),
NEIClientUtils.translate("itempanel.overlay_recipe"));
}
if (handlerRef.canFillCraftingGrid(gui)) {
hotkeys.put(
NEIClientConfig.getKeyName("gui.overlay", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("gui.overlay", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("itempanel.fill_crafting_grid"));
if (handlerRef.allowedTransferAlghoritm(gui)) {
hotkeys.put(
NEIClientConfig.getKeyName(
KeyManager.getKeyName(
"gui.overlay",
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("itempanel.fill_crafting_grid_quantity"));
@@ -558,18 +679,23 @@ public abstract class ShortcutInputHandler {
if (NEIClientConfig.autocraftingEnabled()) {
hotkeys.put(
NEIClientConfig.getKeyName(
KeyManager.getKeyName(
"gui.craft_items",
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("itempanel.craft_missing"));
hotkeys.put(
NEIClientConfig.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
KeyManager.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("itempanel.craft_all"));
}
}
hotkeys.put(
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.recipe.chat_link"));
}
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
return hotkeys;
}
@@ -578,8 +704,10 @@ public abstract class ShortcutInputHandler {
final List<ItemStack> playerInventory = AutoCraftingManager.getInventoryItems(guiContainer).values();
final RecipeId rootRecipeId = math.createMasterRoot();
for (BookmarkItem item : math.recipeIngredients) {
if (item.amount > 0 && rootRecipeId.equals(item.recipeId)) {
for (int i = 0; i < math.recipeIngredients.size(); i++) {
final BookmarkItem item = math.recipeIngredients.get(i);
if (item.getAmount() > 0 && rootRecipeId.equals(item.recipeId)) {
long amount = 0;
for (ItemStack stack : playerInventory) {
@@ -588,43 +716,15 @@ public abstract class ShortcutInputHandler {
}
}
if (amount >= item.amount) {
item.factor = item.amount = amount + item.amount - (amount % item.amount);
if (amount >= item.getAmount()) {
math.recipeIngredients.set(
i,
BookmarkItem.builder(item).multiplier(1)
.factor(amount + item.getAmount() - (amount % item.getAmount()))
.chance(PositionedStack.CHANCE_FULL).build());
}
}
}
}
private static RecipeId getHotkeyRecipeId(GuiContainer gui, int mousex, int mousey, ItemStack stack,
BookmarksGridSlot slot) {
if (slot != null) {
return slot.isIngredient() ? null : slot.getRecipeId();
}
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
}
if (itemSlot == null) {
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
}
if (itemSlot != null) {
return itemSlot.getRecipeId();
}
if (gui instanceof GuiRecipe guiRecipe) {
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
if (focusedRecipe != null) {
return focusedRecipe.getRecipeId();
}
}
return null;
}
}
@@ -0,0 +1,76 @@
package codechicken.nei.asm;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import net.minecraft.client.settings.KeyBinding;
import cpw.mods.fml.relauncher.ReflectionHelper;
public final class KeyBindingFixHooks {
private static volatile boolean initialized;
private static Field keybindArrayField;
private static Field pressedField;
private static Field pressTimeField;
private KeyBindingFixHooks() {}
public static void onTickAll(int keyCode) {
if (keyCode == 0) {
return;
}
for (KeyBinding binding : getBindings()) {
if (binding.getKeyCode() == keyCode) {
try {
pressTimeField.setInt(binding, pressTimeField.getInt(binding) + 1);
} catch (Exception ignored) {}
}
}
}
public static void setKeyBindStateAll(int keyCode, boolean pressed) {
if (keyCode == 0) {
return;
}
for (KeyBinding binding : getBindings()) {
if (binding.getKeyCode() == keyCode) {
try {
pressedField.setBoolean(binding, pressed);
} catch (Exception ignored) {}
}
}
}
@SuppressWarnings("unchecked")
private static List<KeyBinding> getBindings() {
init();
try {
return (List<KeyBinding>) keybindArrayField.get(null);
} catch (Exception ignored) {}
return Collections.emptyList();
}
private static synchronized void init() {
if (initialized) {
return;
}
keybindArrayField = ReflectionHelper.findField(KeyBinding.class, "keybindArray", "field_74516_a");
pressedField = ReflectionHelper.findField(KeyBinding.class, "pressed", "field_74513_e");
pressTimeField = ReflectionHelper.findField(KeyBinding.class, "pressTime", "field_151474_i");
initialized = true;
}
}
@@ -14,6 +14,7 @@ import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.NEW;
import java.util.Map;
@@ -22,11 +23,14 @@ import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import codechicken.lib.asm.ASMBlock;
@@ -56,7 +60,8 @@ public class NEITransformer implements IClassTransformer {
public NEITransformer() {
if (FMLLaunchHandler.side().isClient()) {
// Generates method to set the placed position of a mob spawner for the item callback. More portable than
// Generates method to set the placed position of a mob spawner for the item
// callback. More portable than
// copying vanilla placement code
transformer.add(
new MethodWriter(
@@ -82,7 +87,6 @@ public class NEITransformer implements IClassTransformer {
asmblocks.get("commaFix"),
true));
// fix workbench container losing items on shift click output without room for the full stack
transformer.add(
new MethodTransformer(
new ObfMapping(
@@ -98,6 +102,48 @@ public class NEITransformer implements IClassTransformer {
}
});
if (Boolean.getBoolean("nei.recipeOrigins")) {
// Replace the plain ArrayList CraftingManager's constructor builds its recipe list with, so every recipe
// added anywhere (including vanilla's own, added in that same constructor) is observed for
// codechicken.nei.recipe.debug.RecipeOrigins. Must be done at classload time, since by the time any mod
// code runs vanilla has already populated the list. Capturing a stack trace per recipe is slow, so this is
// opt-in via -Dnei.recipeOrigins=true.
transformer.add(
new MethodTransformer(
new ObfMapping("net/minecraft/item/crafting/CraftingManager", "<init>", "()V")) {
@Override
public void transform(MethodNode mv) {
ASMHelper.logger.debug("NEI: Hooking CraftingManager recipe list for origin tracking");
TypeInsnNode newInsn = null;
MethodInsnNode initInsn = null;
// only the first ArrayList construction, the recipe list field
for (AbstractInsnNode insn : mv.instructions.toArray()) {
if (newInsn == null) {
if (insn.getOpcode() == NEW
&& "java/util/ArrayList".equals(((TypeInsnNode) insn).desc)) {
newInsn = (TypeInsnNode) insn;
}
} else if (insn.getOpcode() == INVOKESPECIAL
&& "java/util/ArrayList".equals(((MethodInsnNode) insn).owner)
&& "<init>".equals(((MethodInsnNode) insn).name)) {
initInsn = (MethodInsnNode) insn;
break;
}
}
if (newInsn == null || initInsn == null) {
ASMHelper.logger.warn("NEI: CraftingManager recipe list not found, origins disabled");
return;
}
newInsn.desc = initInsn.owner = "codechicken/nei/recipe/debug/RecipeOrigins$TrackingList";
}
});
}
// put glint alpha into the buffer correctly for exporting
transformer.add(
new MethodReplacer(
@@ -105,6 +151,33 @@ public class NEITransformer implements IClassTransformer {
asmblocks.get("d_glintAlphaFix"),
asmblocks.get("glintAlphaFix")));
if (FMLLaunchHandler.side().isClient()) {
transformer.add(
new MethodWriter(
ACC_PUBLIC | ACC_STATIC,
new ObfMapping("net/minecraft/client/settings/KeyBinding", "func_74507_a", "(I)V"),
asmblocks.get("m_keyBindingOnTickAll")));
transformer.add(
new MethodWriter(
ACC_PUBLIC | ACC_STATIC,
new ObfMapping("net/minecraft/client/settings/KeyBinding", "func_74510_a", "(IZ)V"),
asmblocks.get("m_keyBindingSetStateAll")));
transformer.add(
new MethodInjector(
new ObfMapping("net/minecraft/client/settings/GameSettings", "func_74303_b", "()V"),
asmblocks.get("saveOptionsHook"),
true));
transformer.add(
new MethodInjector(
new ObfMapping("net/minecraft/client/settings/GameSettings", "func_74303_b", "()V"),
asmblocks.get("n_saveOptionsSendSettings"),
asmblocks.get("saveOptionsRestoreHook"),
false));
}
String GuiContainer = "net/minecraft/client/gui/inventory/GuiContainer";
// add manager field
transformer.add(
@@ -0,0 +1,78 @@
package codechicken.nei.asm;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import codechicken.core.CommonUtils;
import codechicken.nei.KeyManager;
import codechicken.nei.NEIClientConfig;
public final class SaveOptionsFixHooks {
private static final String KEY_PREFIX = "key_nei.options.keys.";
private static List<String> pendingRestoreLines;
private SaveOptionsFixHooks() {}
public static void onBeforeSaveOptions() {
pendingRestoreLines = null;
if (NEIClientConfig.isLoaded()) {
return;
}
final File optionsFile = new File(CommonUtils.getMinecraftDir(), "options.txt");
if (!optionsFile.exists()) {
return;
}
final List<String> missing = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(optionsFile))) {
String line;
while ((line = reader.readLine()) != null) {
if (!line.startsWith(KEY_PREFIX)) continue;
final int colon = line.indexOf(':');
if (colon <= 0) continue;
final String description = line.substring(4, colon);
if (!KeyManager.isRegistered(description)) {
missing.add(line);
}
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to read key bindings before options save", e);
return;
}
if (!missing.isEmpty()) {
pendingRestoreLines = missing;
}
}
public static void onAfterSaveOptions() {
final List<String> restore = pendingRestoreLines;
pendingRestoreLines = null;
if (restore == null) {
return;
}
final File optionsFile = new File(CommonUtils.getMinecraftDir(), "options.txt");
try (FileWriter writer = new FileWriter(optionsFile, true)) {
for (String line : restore) {
writer.write(line);
writer.write(System.lineSeparator());
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to restore NEI key bindings after options save", e);
}
}
}
@@ -30,8 +30,10 @@ import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.recipe.chain.RecipeChainMath;
@@ -88,7 +90,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
public static final Color PLACEHOLDER_COLOR = new Color(0x66222222, true);
public static final int DEFAULT_GROUP_ID = 0;
protected static final int GROUP_PANEL_WIDTH = 7;
public static final int GROUP_PANEL_WIDTH = 7;
protected static final float SCALE_SPEED = 0.1f;
protected List<BookmarkItem> bookmarkItems = new ArrayList<>();
@@ -138,17 +140,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (group.collapsedRecipes.contains(collapsedRecipeId)) {
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId)
&& item.amount == item.factor) {
item.amount = 0;
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.multiplier == 1) {
item.multiplier = 0;
}
}
} else {
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.amount == 0) {
item.amount = item.factor;
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.multiplier == 0) {
item.multiplier = 1;
}
}
@@ -178,11 +179,15 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
public int addGroup(BookmarkGroup group) {
int groupId = this.bookmarkItems.stream().mapToInt(item -> item.groupId).max().orElse(DEFAULT_GROUP_ID) + 1;
final int groupId = getNextGroupId();
this.groups.put(groupId, group);
return groupId;
}
public int getNextGroupId() {
return this.bookmarkItems.stream().mapToInt(item -> item.groupId).max().orElse(DEFAULT_GROUP_ID) + 1;
}
@Override
public int getNumPages() {
@@ -367,19 +372,26 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && item.recipeId != null) {
if (item.recipeId == null && item.type != BookmarkItemType.ITEM) {
item.type = BookmarkItemType.ITEM;
} else if (item.groupId == groupId && item.recipeId != null && item.type != BookmarkItemType.ITEM) {
recipeState.put(
item.recipeId,
recipeState.getOrDefault(item.recipeId, 0) | (item.isIngredient ? 1 : 2));
recipeState.getOrDefault(item.recipeId, 0)
| (item.type == BookmarkItemType.INGREDIENT ? 1 : 2));
}
}
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && item.isIngredient
&& (item.recipeId == null || recipeState.getOrDefault(item.recipeId, 1) == 1)) {
if (item.groupId == groupId && item.type != BookmarkItemType.ITEM
&& recipeState.getOrDefault(item.recipeId, 0) != 3) {
if (item.type == BookmarkItemType.INGREDIENT) {
item.recipeId = null;
}
recipeState.remove(item.recipeId);
item.isIngredient = false;
item.recipeId = null;
item.type = BookmarkItemType.ITEM;
}
}
@@ -405,7 +417,8 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (int index = itemIndex; index < size; index++) {
if (item.equalsRecipe(this.bookmarkItems.get(index))) {
sortingRank.put(index, this.bookmarkItems.get(index).isIngredient ? 1 : -1);
sortingRank
.put(index, this.bookmarkItems.get(index).type == BookmarkItemType.INGREDIENT ? 1 : -1);
items.add(index);
}
}
@@ -439,8 +452,9 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (!this.isEmpty() && this.gridGenerator.maxAbsoluteSlotIndex > 0) {
final GroupingItem groupingItem = ItemPanels.bookmarkPanel.groupingItem;
final int sortableGroupId = ItemPanels.bookmarkPanel.sortableGroup != null
? ItemPanels.bookmarkPanel.sortableGroup.groupId
: -3;
&& ItemPanels.bookmarkPanel.sortableGroup.grid == this
? ItemPanels.bookmarkPanel.sortableGroup.groupId
: -3;
final int rowShift = this.page * this.rows;
Map<Integer, Integer> groups = this.gridGenerator.rowToGroupId;
@@ -591,6 +605,11 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
public RecipeChainMath createRecipeChainMath(int groupId, RecipeId recipeId) {
final BookmarkGroup group = getGroup(groupId);
if (group == null) {
return null;
}
final List<BookmarkItem> chainItems = new ArrayList<>();
final Set<RecipeId> collapsedRecipes = group.crafting != null && recipeId == null ? group.collapsedRecipes
: Collections.emptySet();
@@ -618,20 +637,23 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) {
final BookmarkItem item = this.bookmarkItems.get(itemIndex);
if (item.groupId == groupId && (recipeRelations == null || recipeRelations.contains(item.recipeId))) {
long amount = item.amount;
if (recipeId != null && group.crafting != null
&& group.crafting.calculatedItems.containsKey(itemIndex)
&& recipeId.equals(item.recipeId)) {
amount = group.crafting.calculatedItems.get(itemIndex).getCalculatedAmount();
chainItems.add(
item.copyWithMultiplier(
group.crafting.calculatedItems.get(itemIndex).getCalculatedMultiplier()));
} else {
chainItems.add(item.copy());
}
chainItems.add(item.copyWithAmount(amount));
}
}
if (group.crafting == null) {
for (BookmarkItem item : chainItems) {
item.type = BookmarkItemType.ITEM;
item.recipeId = null;
}
}
@@ -761,6 +783,25 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
int absoluteSlotIndex = absoluteRowIndex * this.columns;
int overGroupId = getRowGroupId(overRowIndex);
// move group to same grid
if (this != sortableGroup.grid) {
final BookmarkGroup sourceGroup = sortableGroup.grid.getGroup(sortableGroup.groupId);
final List<BookmarkItem> itemsToMove = sortableGroup.getBookmarkItems();
sortableGroup.grid.removeGroup(sortableGroup.groupId);
final int groupId = this.addGroup(sourceGroup);
for (BookmarkItem item : itemsToMove) {
item.groupId = groupId;
this.bookmarkItems.add(item);
}
sortableGroup.update(this, groupId);
this.onItemsChanged();
this.getMask();// force generate grid
overGroupId = getRowGroupId(overRowIndex);
}
if (sortableGroup.groupId == overGroupId) {
return;
}
@@ -820,7 +861,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) {
final BookmarkItem item = this.bookmarkItems.get(itemIndex);
if (item.groupId == groupId && item.isIngredient == ingredient
if (item.groupId == groupId && ingredient == (item.type == BookmarkItemType.INGREDIENT)
&& (recipeId == item.recipeId || recipeId != null && recipeId.equals(item.recipeId))
&& StackInfo.equalItemAndNBT(stackA, item.itemStack, true)) {
return itemIndex;
@@ -848,6 +889,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
return this.bookmarkItems.get(itemIndex);
}
public void replaceBookmarkItem(int itemIndex, BookmarkItem newItem) {
if (itemIndex < 0 || itemIndex >= this.bookmarkItems.size()) {
return;
}
this.bookmarkItems.set(itemIndex, newItem);
onItemsChanged();
}
public int getAbsoluteSlotIndex(int itemIndex) {
return this.gridGenerator.itemToSlot.getOrDefault(itemIndex, -1);
}
@@ -867,46 +918,55 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
public void addRecipe(Recipe recipe, int multiplier, int groupId) {
final RecipeId recipeId = recipe.getRecipeId();
final List<ItemStack> results = recipe.getResults().stream().map(res -> res.getItemStack())
.collect(Collectors.toList());
final List<ItemStack> ingredients = recipe.getIngredients().stream().map(ingr -> ingr.getItemStack())
.collect(Collectors.toList());
final ItemStackAmount seenResults = new ItemStackAmount();
final ItemStackAmount seenIngredients = new ItemStackAmount();
final List<ItemStack> results = new ArrayList<>();
final List<ItemStack> ingredients = new ArrayList<>();
for (ItemStack stack : ItemStackAmount.of(results).values()) {
long factor = StackInfo.getAmount(stack);
for (RecipeIngredient result : recipe.getResults()) {
ItemStack stack = result.getItemStack();
if (!seenResults.contains(stack)) {
seenResults.add(stack);
results.add(stack);
}
}
for (RecipeIngredient ingr : recipe.getIngredients()) {
ItemStack stack = ingr.getItemStack();
if (!seenIngredients.contains(stack)) {
seenIngredients.add(stack);
ingredients.add(stack);
}
}
for (ItemStack stack : results) {
this.addItem(
BookmarkItem.of(groupId, StackInfo.withAmount(stack, factor * multiplier), factor, recipeId, false),
BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT).multiplier(multiplier)
.build(),
true);
}
for (ItemStack stack : ItemStackAmount.of(ingredients).values()) {
long factor = StackInfo.getAmount(stack);
for (ItemStack stack : ingredients) {
this.addItem(
BookmarkItem.of(
groupId,
StackInfo.withAmount(stack, factor * multiplier),
factor,
recipeId,
true,
BookmarkItem.generatePermutations(stack, recipe)),
BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.INGREDIENT).multiplier(multiplier)
.build(),
true);
}
}
public void removeRecipe(int itemIndex, boolean removeFullRecipe) {
final BookmarkItem item = getCalculatedItem(itemIndex);
final BookmarkItem item = getBookmarkItem(itemIndex);
if (item == null) {
return;
}
if (!removeFullRecipe && item.recipeId != null && !item.isIngredient) {
if (!removeFullRecipe && item.recipeId != null && item.type == BookmarkItemType.RESULT) {
removeFullRecipe = this.bookmarkItems.stream()
.noneMatch(m -> !m.isIngredient && !m.equals(item) && item.equalsRecipe(m));
.noneMatch(m -> m.type == BookmarkItemType.RESULT && !m.equals(item) && item.equalsRecipe(m));
}
if (item.recipeId != null && removeFullRecipe) {
if (item.recipeId != null && item.type != BookmarkItemType.ITEM && removeFullRecipe) {
removeRecipe(item.recipeId, item.groupId);
} else if (itemIndex >= 0) {
this.bookmarkItems.remove(itemIndex);
@@ -925,7 +985,8 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
public boolean existsRecipe(RecipeId recipeId, int groupId) {
return this.bookmarkItems.stream().anyMatch(item -> item.equalsRecipe(recipeId, groupId));
return this.bookmarkItems.stream()
.anyMatch(item -> item.type != BookmarkItemType.ITEM && item.equalsRecipe(recipeId, groupId));
}
public RecipeId getRecipeId(int itemIndex) {
@@ -1028,8 +1089,14 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
if (bookmarkItem != null && (group.crafting != null && group.viewMode == BookmarkViewMode.DEFAULT
|| group.viewMode == BookmarkViewMode.TODO_LIST && !sortableItem.bookmarkItem.isIngredient)) {
while (sortIndex > 0 && bookmarkItem.equalsRecipe(getCalculatedItem(sortedIndexes.get(sortIndex - 1)))) {
|| group.viewMode == BookmarkViewMode.TODO_LIST
&& sortableItem.bookmarkItem.type == BookmarkItemType.RESULT)) {
BookmarkItem prevBookmarkItem = sortIndex > 1 ? getCalculatedItem(sortedIndexes.get(sortIndex - 1)) : null;
while (sortIndex > 0 && prevBookmarkItem != null
&& prevBookmarkItem.type != BookmarkItemType.ITEM
&& bookmarkItem.equalsRecipe(prevBookmarkItem)) {
prevBookmarkItem = sortIndex > 1 ? getCalculatedItem(sortedIndexes.get(sortIndex - 1)) : null;
sortIndex--;
}
@@ -1058,7 +1125,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (nextSortIndex < sortedIndexes.size()) {
final int nextItemIndex = sortedIndexes.get(nextSortIndex);
if (group.viewMode == BookmarkViewMode.TODO_LIST && !target.isIngredient
if (group.viewMode == BookmarkViewMode.TODO_LIST && target.type == BookmarkItemType.RESULT
&& target.equalsRecipe(getCalculatedItem(nextItemIndex))) {
return -1;
}
@@ -1092,8 +1159,8 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
final Set<Long> sizes = new HashSet<>();
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == targetGroupId && item.amount > 0) {
sizes.add(Math.max(item.getStackSize(), item.factor / item.fluidCellAmount));
if (item.groupId == targetGroupId && item.getAmount() > 0) {
sizes.add(Math.max(item.getStackSize(), item.getStackSize(item.getAmount(1))));
items.add(item);
}
}
@@ -1102,14 +1169,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
long multiplier = Integer.MAX_VALUE;
for (BookmarkItem item : items) {
long customFactor = (long) Math.ceil(item.amount / (double) gcd);
long customMultiplier = (long) (customFactor > 0 ? Math.ceil(item.amount / (double) customFactor) : 0);
final long amount = item.getAmount();
final long customFactor = (amount + gcd - 1) / gcd;
final long customMultiplier = customFactor > 0 ? ((amount + customFactor - 1) / customFactor) : 0;
multiplier = Math.min(shiftMultiplier(customMultiplier, shift, 1), multiplier);
}
for (BookmarkItem item : items) {
long customFactor = (long) Math.ceil(item.amount / (double) gcd);
item.amount = customFactor * multiplier;
final long amount = item.getAmount();
final long customFactor = (amount + gcd - 1) / gcd;
item.multiplier = item.getMultiplierFromAmount(customFactor * multiplier);
}
} else {
final Map<RecipeId, Long> recipeMultipliers = new HashMap<>();
@@ -1117,7 +1186,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
final List<BookmarkItem> items = new ArrayList<>();
for (BookmarkItem item : this.bookmarkItems) {
if (item.factor > 0
if (!item.emptyFactor()
&& outputRecipes.stream().anyMatch(recipeId -> item.equalsRecipe(recipeId, targetGroupId))) {
final long multiplier = recipeMultipliers.getOrDefault(item.recipeId, (long) Integer.MAX_VALUE);
final boolean recipeInMiddle = group.crafting != null
@@ -1127,7 +1196,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
item.recipeId,
Math.min(
shiftMultiplier(
Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()),
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
shift,
recipeInMiddle ? 1 : 0),
multiplier));
@@ -1136,7 +1205,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
}
for (BookmarkItem item : items) {
item.amount = item.factor * recipeMultipliers.getOrDefault(item.recipeId, 0L);
item.multiplier = recipeMultipliers.getOrDefault(item.recipeId, 0L);
}
}
@@ -1149,38 +1218,46 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
if (group.collapsed) {
shiftGroupAmount(targetItem.groupId, shift);
} else if (targetItem.recipeId != null) {
final RecipeId recipeId = this.gridGenerator.itemToRecipe
.getOrDefault(targetItemIndex, targetItem.recipeId);
final boolean recipeInMiddle = group.crafting != null && group.crafting.recipeInMiddle.contains(recipeId);
long multiplier = Integer.MAX_VALUE;
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetItem.groupId) && item.factor > 0) {
multiplier = Math.min(
shiftMultiplier(
Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()),
shift,
recipeInMiddle ? 1 : 0),
multiplier);
}
}
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetItem.groupId) && item.factor > 0) {
item.amount = item.factor * multiplier;
}
}
} else if (targetItem.recipeId != null && targetItem.type != BookmarkItemType.ITEM) {
shiftRecipeAmount(
targetItem.groupId,
this.gridGenerator.itemToRecipe.getOrDefault(targetItemIndex, targetItem.recipeId),
shift);
} else {
for (BookmarkItem item : this.bookmarkItems) {
if (targetItem.equals(item)) {
item.amount = item.factor * shiftMultiplier(item.getMultiplier(), shift, 0);
item.multiplier = shiftMultiplier(item.multiplier, shift, 0);
break;
}
}
this.onItemsChanged();
}
}
public void shiftRecipeAmount(int targetGroupId, RecipeId recipeId, long shift) {
final BookmarkGroup group = getGroup(targetGroupId);
final boolean recipeInMiddle = group != null && group.crafting != null
&& group.crafting.recipeInMiddle.contains(recipeId);
long multiplier = Integer.MAX_VALUE;
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetGroupId) && !item.emptyFactor()) {
multiplier = Math.min(
shiftMultiplier(
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
shift,
recipeInMiddle ? 1 : 0),
multiplier);
}
}
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetGroupId) && !item.emptyFactor()) {
item.multiplier = multiplier;
}
}
this.onItemsChanged();
@@ -1208,7 +1285,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (BookmarksGridSlot slot : getMask()) {
Rectangle4i rect = getSlotRect(slot.slotIndex);
if (sortableGroup != null && slot.getGroupId() == sortableGroup.groupId) {
if (sortableGroup != null && sortableGroup.matches(this, slot.getGroupId())) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR);
} else if (sortableItem != null && sortableItem.items.contains(slot.itemIndex)) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR);
@@ -1237,7 +1314,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem;
for (BookmarksGridSlot slot : getMask()) {
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
slot.afterDraw(getSlotRect(slot.slotIndex), mouseContext);
}
@@ -1254,7 +1331,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
for (BookmarksGridSlot slot : getMask()) {
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
final BookmarkItem item = slot.getBookmarkItem();
@@ -11,6 +11,7 @@ import java.util.stream.Collectors;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemSorter;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.RecipeChainDetails.BookmarkChainItem;
import codechicken.nei.bookmark.RecipeChainDetails.CalculatedType;
import codechicken.nei.recipe.Recipe.RecipeId;
@@ -65,10 +66,14 @@ public class BookmarkGridGenerator {
while (itemIndex < size && absoluteSlotIndex != -1) {
final BookmarkItem bookmarkItem = this.grid.getBookmarkItem(itemIndex);
final BookmarkGroup group = this.grid.getGroup(bookmarkItem.groupId);
final boolean isCollapsedRecipeAnchor = group.crafting != null && bookmarkItem.recipeId != null
&& bookmarkItem.type != BookmarkItemType.INGREDIENT
&& group.collapsedRecipes.contains(bookmarkItem.recipeId);
if (group.crafting != null && (!group.crafting.calculatedItems.containsKey(itemIndex)
|| bookmarkItem.recipeId != null && !bookmarkItem.recipeId
.equals(group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId)))) {
if (group.crafting != null && !isCollapsedRecipeAnchor
&& (!group.crafting.calculatedItems.containsKey(itemIndex)
|| bookmarkItem.recipeId != null && !bookmarkItem.recipeId.equals(
group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId)))) {
itemIndex++;
} else if (group.collapsed) {
final List<Integer> results = getGroupResults(bookmarkItem.groupId, itemIndex, size);
@@ -117,7 +122,7 @@ public class BookmarkGridGenerator {
this.itemToSlot.put(collapsedItemIndex, absoluteSlotIndex);
this.caclulatedItems.put(collapsedItemIndex, getItem(group, collapsedItemIndex));
if (!(shadows = getShadows(group, item.recipeId)).isEmpty()) {
if (item.recipeId != null && !(shadows = getShadows(group, item.recipeId)).isEmpty()) {
borderGrid.put(absoluteSlotIndex, "recipe:" + shadows.get(0));
}
}
@@ -165,7 +170,7 @@ public class BookmarkGridGenerator {
break;
}
if (!bookmarkItem.isIngredient
if (bookmarkItem.type != BookmarkItemType.INGREDIENT
&& (group.crafting == null || group.crafting.calculatedItems.containsKey(itemIndex))) {
results.add(itemIndex);
}
@@ -188,13 +193,11 @@ public class BookmarkGridGenerator {
for (int shadowIndex : shadows) {
final boolean isIngredient = shadows.get(0) != shadowIndex
|| group.crafting.calculatedItems.get(shadowIndex).calculatedType == CalculatedType.INGREDIENT;
final BookmarkItem shadowBookmarkItem = BookmarkItem.of(
bookmarkItem.groupId,
bookmarkItem.itemStack,
1,
bookmarkItem.recipeId,
isIngredient,
Collections.emptyMap());
final BookmarkItem shadowBookmarkItem = BookmarkItem.builder(bookmarkItem.groupId, bookmarkItem.itemStack)
.multiplier(1).factor(StackInfo.getAmount(bookmarkItem.itemStack)).recipeId(bookmarkItem.recipeId)
.type(isIngredient ? BookmarkItemType.INGREDIENT : BookmarkItemType.RESULT)
.permutations(Collections.emptyMap()).build();
if ((absoluteSlotIndex = nextSlotIndex(
absoluteSlotIndex + 1,
@@ -227,7 +230,7 @@ public class BookmarkGridGenerator {
index = Math.max(0, index);
while (j++ < maxIndex) {
boolean isFirstColumn = (index % gridColumns) == 0;
final boolean isFirstColumn = (index % gridColumns) == 0;
if (this.grid.isInvalidSlot(index % maxIndex)) {
index++;
@@ -238,17 +241,22 @@ public class BookmarkGridGenerator {
return index;
} else if (isFirstColumn && (meta.recipeId == null || previousMeta == null
|| previousMeta.groupId != meta.groupId
|| meta.type != BookmarkItemType.INGREDIENT
|| !meta.recipeId.equals(previousMeta.recipeId)
|| index + 1 < maxIndex && this.grid.isInvalidSlot((index + 1) % maxIndex))) {
// In first column must be an item without recipe, a recipe result, or an ingredient
// if the second column is occupied
return index;
} else
if (!isFirstColumn && meta.recipeId != null && meta.recipeId.equals(previousMeta.recipeId)) {
return index;
} else {
index++;
}
if (!isFirstColumn && meta.type != BookmarkItemType.ITEM
&& previousMeta.type != BookmarkItemType.ITEM
&& meta.groupId == previousMeta.groupId
&& meta.recipeId != null
&& meta.recipeId.equals(previousMeta.recipeId)) {
return index;
} else {
index++;
}
}
@@ -314,7 +322,7 @@ public class BookmarkGridGenerator {
return group.crafting.calculatedItems.get(itemIndex);
} else {
BookmarkChainItem item = BookmarkChainItem.of(this.grid.getBookmarkItem(itemIndex));
item.setRealAmount(item.getShiftAmount());
item.setRealMultiplier(item.getCalculatedMultiplier());
return item;
}
}
@@ -327,22 +335,24 @@ public class BookmarkGridGenerator {
return new BookmarksGridSlot(
slotIndex,
itemIndex,
chainItem.getRealAmount(),
chainItem.getRealMultiplier(),
0,
0,
chainItem.getItem(),
null,
group);
group,
this.grid);
} else {
return new BookmarksGridSlot(
slotIndex,
itemIndex,
chainItem.getRealAmount(),
chainItem.getRealMultiplier(),
chainItem.getShiftAmount(),
chainItem.getCalculatedAmount(),
chainItem.getCalculatedMultiplier(),
chainItem.getItem(),
chainItem.calculatedType,
group);
group,
this.grid);
}
}
@@ -2,6 +2,7 @@ package codechicken.nei.bookmark;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -9,147 +10,407 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.FluidStack;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
public class BookmarkItem {
private static Map<ItemStack, String> fuzzyPermutations = new HashMap<>();
public static class Builder {
private static Map<ItemStack, String> fuzzyPermutations = new HashMap<>();
private final int groupId;
private final ItemStack stack;
private long factor;
private long multiplier = -1;
private long chance = PositionedStack.CHANCE_FULL;
private RecipeId recipeId = null;
private Recipe recipe = null;
private BookmarkItemType type = BookmarkItemType.ITEM;
private Map<String, ItemStack> permutations = null;
private Builder(int groupId, ItemStack stack) {
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack);
this.groupId = groupId;
this.stack = stack;
this.factor = StackInfo.isFluidDisplayItem(stack) ? Math.max(nbTag.getInteger("Count"), 1) : 1;
}
public Builder factor(long factor) {
this.factor = factor;
return this;
}
public Builder multiplier(long multiplier) {
this.multiplier = multiplier;
return this;
}
public Builder chance(long chance) {
this.chance = chance;
return this;
}
public Builder recipe(Recipe recipe) {
this.recipe = recipe;
this.recipeId = null;
return this;
}
public Builder recipeId(RecipeId recipeId) {
this.recipeId = recipeId;
this.recipe = null;
return this;
}
public Builder type(BookmarkItemType type) {
this.type = type;
return this;
}
public Builder permutations(Map<String, ItemStack> permutations) {
this.permutations = permutations;
return this;
}
public BookmarkItem build() {
final Map<String, ItemStack> perms;
long multiplier = 0;
if (this.permutations != null) {
perms = this.permutations;
} else if (this.type == BookmarkItemType.INGREDIENT) {
if (this.recipe != null) {
perms = Builder.generatePermutations(this.stack, this.recipe);
} else if (this.recipeId != null) {
perms = Builder.generatePermutations(this.stack, Recipe.of(this.recipeId));
} else {
perms = Builder.generatePermutations(this.stack);
}
} else {
perms = Builder.generatePermutations(this.stack);
}
if (this.multiplier >= 0) {
multiplier = this.multiplier;
} else if (this.factor > 0) {
multiplier = StackInfo.getAmount(this.stack) / this.factor;
}
return new BookmarkItem(
this.groupId,
multiplier,
this.factor,
this.chance,
this.stack,
perms,
this.recipeId != null ? this.recipeId : this.recipe != null ? this.recipe.getRecipeId() : null,
this.type);
}
public static Map<String, ItemStack> generatePermutations(ItemStack stack) {
return Collections.singletonMap(getItemGUID(stack), stack);
}
public static Map<String, ItemStack> generatePermutations(ItemStack stack, Recipe recipe) {
if (recipe != null) {
final RecipeIngredient ingr = recipe.getIngredients().stream()
.filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null);
if (ingr != null) {
final Map<String, ItemStack> permutations = new HashMap<>();
for (ItemStack ingrStack : ingr.getPermutations()) {
permutations.put(getItemGUID(ingrStack), ingrStack);
}
return permutations;
}
}
return generatePermutations(stack);
}
private static synchronized String getItemGUID(ItemStack stack) {
final FluidStack fluidStack = StackInfo.getFluid(stack);
if (fluidStack != null && (fluidStack.amount > 0 || !StackInfo.isFluidContainer(stack))) {
return fluidStack.getFluid().getName() + ":" + fluidStack.tag;
} else {
for (Map.Entry<ItemStack, String> entry : Builder.fuzzyPermutations.entrySet()) {
if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, entry.getKey())) {
Builder.fuzzyPermutations.put(stack, entry.getValue());
return entry.getValue();
}
}
final String stackGUID = StackInfo.getItemStackGUID(stack);
Builder.fuzzyPermutations.put(stack, stackGUID);
return stackGUID;
}
}
}
public enum BookmarkItemType {
INGREDIENT,
RESULT,
ITEM;
public static BookmarkItemType fromInt(int type) {
return type == 0 ? ITEM : type == 1 ? RESULT : INGREDIENT;
}
public int toInt() {
return this == ITEM ? 0 : this == RESULT ? 1 : 2;
}
}
public int groupId;
public long amount = 0L;
public final int fluidCellAmount;
public ItemStack itemStack;
public Map<String, ItemStack> permutations;
public long factor;
public RecipeId recipeId;
public boolean isIngredient = false;
public BookmarkItemType type = BookmarkItemType.ITEM;
public long multiplier = 1L;
public final long factor;
public final int fluidCellAmount;
public final long chance;
protected BookmarkItem(int groupId, long multiplier, long factor, long chance, ItemStack itemStack,
Map<String, ItemStack> permutations, RecipeId recipeId, BookmarkItemType type) {
final FluidStack fluidStack = StackInfo.getFluid(itemStack);
if (fluidStack != null) {
this.fluidCellAmount = Math.max(1, StackInfo.isFluidContainer(itemStack) ? fluidStack.amount : 1);
} else {
this.fluidCellAmount = 1;
}
protected BookmarkItem(int groupId, long amount, int fluidCellAmount, ItemStack itemStack,
Map<String, ItemStack> permutations, long factor, RecipeId recipeId, boolean isIngredient) {
this.groupId = groupId;
this.amount = amount;
this.fluidCellAmount = fluidCellAmount;
this.itemStack = itemStack;
this.permutations = permutations;
this.factor = factor;
this.recipeId = recipeId;
this.isIngredient = isIngredient;
this.type = type == null ? BookmarkItemType.ITEM : type;
this.multiplier = multiplier;
this.factor = factor;
this.chance = chance;
}
public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, boolean isIngredient,
Map<String, ItemStack> permutations) {
final FluidStack fluidStack = StackInfo.getFluid(stack);
int fluidCellAmount = 1;
long amount;
public static Builder builder(int groupId, ItemStack stack) {
return new Builder(groupId, stack);
}
if (fluidStack != null) {
amount = fluidStack.amount * (long) Math.max(0, stack.stackSize);
fluidCellAmount = Math.max(1, StackInfo.isFluidContainer(stack) ? fluidStack.amount : 1);
} else {
amount = StackInfo.getAmount(stack);
public static Builder builder(BookmarkItem item) {
return new Builder(item.groupId, item.itemStack).factor(item.factor).multiplier(item.multiplier)
.chance(item.chance).recipeId(item.recipeId).type(item.type).permutations(item.permutations);
}
public static Builder builder(int groupId, ItemStack stack, Recipe recipe, BookmarkItemType type) {
final List<RecipeIngredient> items = type == BookmarkItemType.RESULT ? recipe.getResults()
: recipe.getIngredients();
final Builder builder = builder(groupId, stack).type(type).recipe(recipe);
final ItemStack emptyStack = StackInfo.withAmount(stack, 0);
long amount = 0;
for (RecipeIngredient res : items) {
for (ItemStack perm : res.getPermutations()) {
if (NEIClientUtils.areStacksSameTypeWithNBT(emptyStack, StackInfo.withAmount(perm, 0))) {
amount += 1L * StackInfo.getAmount(perm) * res.getChance();
break;
}
}
}
return new BookmarkItem(
groupId,
amount,
fluidCellAmount,
stack,
permutations,
factor * fluidCellAmount,
recipeId,
isIngredient);
}
if ((amount % PositionedStack.CHANCE_FULL) == 0) {
builder.factor(amount / PositionedStack.CHANCE_FULL);
} else {
builder.factor(1).chance(amount);
}
public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, boolean isIngredient) {
return of(groupId, stack, factor, recipeId, isIngredient, generatePermutations(stack, recipeId, isIngredient));
return builder;
}
public static BookmarkItem of(int groupId, ItemStack stack) {
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack);
final long factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1;
return BookmarkItem
.of(groupId, stack, factor, null, false, Collections.singletonMap(getItemGUID(stack), stack));
return builder(groupId, stack).build();
}
public static BookmarkItem of(JsonObject jsonObject) {
final NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item"));
final ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT);
if (itemStack != null) {
final int groupId = jsonObject.has("groupId") ? jsonObject.get("groupId").getAsInt()
: BookmarkGrid.DEFAULT_GROUP_ID;
final BookmarkItem.Builder builder = BookmarkItem.builder(groupId, itemStack);
if (jsonObject.has("type")) {
builder.type(BookmarkItemType.fromInt(jsonObject.get("type").getAsInt()));
} else if (jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()) {
builder.type(BookmarkItemType.INGREDIENT);
} else {
builder.type(BookmarkItemType.RESULT);
}
if (jsonObject.has("multiplier")) {
builder.multiplier(jsonObject.get("multiplier").getAsInt());
}
if (jsonObject.has("factor")) {
builder.factor(jsonObject.get("factor").getAsInt());
}
if (jsonObject.has("chance")) {
builder.chance(jsonObject.get("chance").getAsInt());
}
if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) {
builder.recipeId(RecipeId.of(recipeJson));
}
return builder.build();
}
return null;
}
public BookmarkItem copyWithMultiplier(long multiplier) {
return new BookmarkItem(
this.groupId,
multiplier,
this.factor,
this.chance,
this.itemStack,
this.permutations,
this.recipeId,
this.type);
}
public BookmarkItem copyWithAmount(long amount) {
return new BookmarkItem(
this.groupId,
amount,
this.fluidCellAmount,
this.itemStack,
this.permutations,
this.factor,
this.recipeId,
this.isIngredient);
return copyWithMultiplier(getMultiplierFromAmount(amount));
}
public BookmarkItem copy() {
return copyWithAmount(this.amount);
return copyWithMultiplier(this.multiplier);
}
public static Map<String, ItemStack> generatePermutations(ItemStack stack, RecipeId recipeId,
boolean isIngredient) {
public BookmarkItem copyWithPerm(ItemStack stack) {
if (isIngredient && recipeId != null) {
return generatePermutations(stack, Recipe.of(recipeId));
}
if (this.recipeId != null
&& !NEIClientUtils.areStacksSameTypeWithNBT(StackInfo.withAmount(stack, 0), this.getItemStack(0))) {
final ItemStack oldPerm = findPermutation(this.itemStack);
final ItemStack newPerm = findPermutation(stack);
return Collections.singletonMap(getItemGUID(stack), stack);
}
if (oldPerm != null && newPerm != null) {
final long oldAmount = StackInfo.getAmount(oldPerm);
final long amount = oldAmount > 0 ? this.factor * this.chance * StackInfo.getAmount(newPerm) / oldAmount
: 0;
final long factor = (amount % PositionedStack.CHANCE_FULL) == 0 ? amount / PositionedStack.CHANCE_FULL
: 1;
final long chance = factor == 1 ? amount : PositionedStack.CHANCE_FULL;
public static Map<String, ItemStack> generatePermutations(ItemStack stack, Recipe recipe) {
if (recipe != null) {
final RecipeIngredient ingr = recipe.getIngredients().stream()
.filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null);
if (ingr != null) {
final Map<String, ItemStack> permutations = new HashMap<>();
for (ItemStack ingrStack : ingr.getPermutations()) {
permutations.put(getItemGUID(ingrStack), ingrStack);
}
return permutations;
return new BookmarkItem(
this.groupId,
this.multiplier,
factor,
chance,
newPerm,
this.permutations,
this.recipeId,
this.type);
}
}
return Collections.singletonMap(getItemGUID(stack), stack);
return copy();
}
private static synchronized String getItemGUID(ItemStack stack) {
final FluidStack fluidStack = StackInfo.getFluid(stack);
private ItemStack findPermutation(ItemStack stack) {
if (fluidStack != null) {
return fluidStack.getFluid().getName() + ":" + fluidStack.tag;
} else {
for (Map.Entry<ItemStack, String> entry : BookmarkItem.fuzzyPermutations.entrySet()) {
if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, entry.getKey())) {
BookmarkItem.fuzzyPermutations.put(stack, entry.getValue());
return entry.getValue();
}
for (ItemStack itemStack : this.permutations.values()) {
if (StackInfo.equalItemAndNBT(stack, itemStack, true)) {
return itemStack;
}
final String stackGUID = StackInfo.getItemStackGUID(stack);
BookmarkItem.fuzzyPermutations.put(stack, stackGUID);
return stackGUID;
}
return null;
}
public boolean containsItems(BookmarkItem item) {
return this.permutations.keySet().stream().anyMatch(item.permutations::containsKey);
}
public long getAmount() {
return getAmount(this.multiplier);
}
public long getAmount(long multiplier, ItemStack perm) {
if (this.recipeId != null && perm != null
&& !NEIClientUtils.areStacksSameTypeWithNBT(StackInfo.withAmount(perm, 0), this.getItemStack(0))) {
final ItemStack oldPerm = findPermutation(this.itemStack);
final ItemStack newPerm = findPermutation(perm);
if (oldPerm != null && newPerm != null) {
final long oldAmount = StackInfo.getAmount(oldPerm);
final long amount = oldAmount > 0 ? this.factor * this.chance * StackInfo.getAmount(newPerm) / oldAmount
: 0;
final long factor = (amount % PositionedStack.CHANCE_FULL) == 0 ? amount / PositionedStack.CHANCE_FULL
: 1;
final long chance = factor == 1 ? amount : PositionedStack.CHANCE_FULL;
return getAmount(multiplier, factor, chance);
}
}
return getAmount(multiplier);
}
public long getAmount(long multiplier) {
return getAmount(multiplier, this.factor, this.chance);
}
protected long getAmount(long multiplier, long factor, long chance) {
long amount = factor * multiplier;
if (this.fluidCellAmount > 1) {
amount = amount * this.fluidCellAmount;
}
if (chance != PositionedStack.CHANCE_FULL) {
if (this.type == BookmarkItemType.INGREDIENT) {
// For ingredients, round up the expected amount to be conservative.
return (amount * chance + PositionedStack.CHANCE_FULL - 1) / PositionedStack.CHANCE_FULL;
} else {
// For results, round down the expected amount to be conservative.
return (amount * chance) / PositionedStack.CHANCE_FULL;
}
}
return amount;
}
public ItemStack getItemStack() {
return getItemStack(this.amount);
return getItemStack(getAmount());
}
public ItemStack getItemStack(long amount) {
@@ -157,23 +418,34 @@ public class BookmarkItem {
}
public long getStackSize() {
return getStackSize(this.amount);
return getStackSize(getAmount());
}
public long getStackSize(long amount) {
return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.fluidCellAmount) : 0);
if (this.fluidCellAmount > 1) {
amount = (amount + this.fluidCellAmount - 1) / this.fluidCellAmount;
}
return amount;
}
public long getMultiplierFromAmount(long amount) {
if (this.factor <= 0 || this.chance <= 0) return 0;
if (this.fluidCellAmount > 1) {
amount = (amount + this.fluidCellAmount - 1) / this.fluidCellAmount;
}
if (this.chance == PositionedStack.CHANCE_FULL) {
return (amount + this.factor - 1) / this.factor;
}
return (amount * PositionedStack.CHANCE_FULL + this.factor * this.chance - 1) / (this.factor * this.chance);
}
public long getMultiplier() {
return getMultiplier(this.amount);
}
public long getMultiplier(long amount) {
return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.factor) : 0);
}
public long getFactor() {
return this.factor / this.fluidCellAmount;
return this.multiplier;
}
public boolean equalsRecipe(BookmarkItem meta) {
@@ -184,16 +456,20 @@ public class BookmarkItem {
return groupId == this.groupId && recipeId != null && recipeId.equals(this.recipeId);
}
public boolean emptyFactor() {
return this.factor <= 0 || this.chance <= 0;
}
@Override
public int hashCode() {
return Objects.hash(this.groupId, this.fluidCellAmount, this.isIngredient, this.recipeId);
return Objects.hash(this.groupId, this.fluidCellAmount, this.type.toInt(), this.recipeId);
}
@Override
public boolean equals(Object object) {
if (object instanceof BookmarkItem item) {
return this.groupId == item.groupId && this.isIngredient == item.isIngredient
return this.groupId == item.groupId && this.type == item.type
&& this.fluidCellAmount == item.fluidCellAmount
&& StackInfo.equalItemAndNBT(this.itemStack, item.itemStack, true)
&& (this.recipeId == item.recipeId || this.recipeId != null && this.recipeId.equals(item.recipeId));
@@ -202,4 +478,27 @@ public class BookmarkItem {
return false;
}
public JsonObject toJsonObject() {
final JsonObject jsonObject = new JsonObject();
jsonObject.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(getItemStack())));
jsonObject.add("multiplier", new JsonPrimitive(this.multiplier));
jsonObject.add("factor", new JsonPrimitive(this.factor));
jsonObject.add("type", new JsonPrimitive(this.type.toInt()));
if (this.groupId != BookmarkGrid.DEFAULT_GROUP_ID) {
jsonObject.add("groupId", new JsonPrimitive(this.groupId));
}
if (this.recipeId != null) {
jsonObject.add("recipeId", this.recipeId.toJsonObject());
}
if (this.chance != PositionedStack.CHANCE_FULL) {
jsonObject.add("chance", new JsonPrimitive(this.chance));
}
return jsonObject;
}
}
@@ -0,0 +1,283 @@
package codechicken.nei.bookmark;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import com.google.gson.JsonObject;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemQuantityField;
import codechicken.nei.ItemStackSet;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
public class BookmarkPayload {
private String linkType;
private NBTTagCompound target;
private NBTTagCompound group;
private NBTTagList items = new NBTTagList();
private NBTTagList recipes = new NBTTagList();
private BookmarkPayload(String linkType, ItemStack stack) {
this.linkType = linkType;
this.target = stack != null ? StackInfo.itemStackToNBT(stack) : null;
}
public static BookmarkPayload of(NBTTagCompound nbt) {
final BookmarkPayload payload = new BookmarkPayload(nbt.getString("linkType"), null);
if (nbt.hasKey("item")) {
payload.target = nbt.getCompoundTag("item");
}
if (nbt.hasKey("group")) {
payload.group = nbt.getCompoundTag("group");
}
payload.items = nbt.getTagList("items", 10);
payload.recipes = nbt.getTagList("recipes", 10);
return payload;
}
public static BookmarkPayload of(BookmarkItem item) {
final BookmarkPayload payload = new BookmarkPayload("recipe", item.getItemStack());
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
final RecipeId recipeId = item.recipeId;
final int groupId = item.groupId;
payload.items = new NBTTagList();
payload.recipes = new NBTTagList();
if (recipeId != null) {
for (int itemIndex = 0; itemIndex < grid.size(); itemIndex++) {
final BookmarkItem currentItem = grid.getBookmarkItem(itemIndex);
if (currentItem != null && currentItem.groupId == groupId && recipeId.equals(currentItem.recipeId)) {
addBookmarkItemToPayload(payload, currentItem, recipesIndexes);
}
}
} else {
addBookmarkItemToPayload(payload, item, recipesIndexes);
}
return payload;
}
public static BookmarkPayload of(Recipe recipe) {
final BookmarkPayload payload = new BookmarkPayload("recipe", recipe.getResult());
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
final ItemStackSet seenIngredients = new ItemStackSet();
final ItemStackSet seenResults = new ItemStackSet();
for (RecipeIngredient result : recipe.getResults()) {
ItemStack stack = result.getItemStack();
if (!seenResults.contains(stack)) {
final BookmarkItem item = BookmarkItem.builder(-1, stack, recipe, BookmarkItemType.RESULT).build();
addBookmarkItemToPayload(payload, item, recipesIndexes);
seenResults.add(stack);
}
}
for (RecipeIngredient ingr : recipe.getIngredients()) {
ItemStack stack = ingr.getItemStack();
if (!seenIngredients.contains(stack)) {
final BookmarkItem item = BookmarkItem.builder(-1, stack, recipe, BookmarkItemType.INGREDIENT).build();
addBookmarkItemToPayload(payload, item, recipesIndexes);
seenIngredients.add(stack);
}
}
return payload;
}
public static BookmarkPayload of(int groupId) {
final BookmarkPayload payload = new BookmarkPayload("group", null);
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
for (int itemIndex = 0; itemIndex < grid.size(); itemIndex++) {
final BookmarkItem item = grid.getBookmarkItem(itemIndex);
if (item != null && item.groupId == groupId) {
addBookmarkItemToPayload(payload, item, recipesIndexes);
}
}
payload.group = generateGroup(grid.getGroup(groupId));
return payload;
}
public static BookmarkPayload of(ItemStack stackover, RecipeId recipeId) {
final BookmarkPayload payload = new BookmarkPayload("item", stackover);
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
final Point mousePos = GuiDraw.getMousePosition();
if (ItemPanels.itemPanel.containsWithSubpanels(mousePos.x, mousePos.y)) {
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
}
final BookmarkItem item = BookmarkItem.builder(-1, stackover).recipeId(recipeId).type(BookmarkItemType.ITEM)
.build();
addBookmarkItemToPayload(payload, item, recipesIndexes);
return payload;
}
private static NBTTagCompound generateGroup(BookmarkGroup group) {
final NBTTagCompound groupTag = new NBTTagCompound();
final NBTTagList collapsedRecipes = new NBTTagList();
groupTag.setString("viewmode", group.viewMode.toString());
groupTag.setBoolean("crafting", group.crafting != null);
groupTag.setBoolean("collapsed", group.collapsed);
for (RecipeId recipeId : group.collapsedRecipes) {
collapsedRecipes.appendTag(NBTJson.toNbt(recipeId.toJsonObject()));
}
groupTag.setTag("collapsedRecipes", collapsedRecipes);
return groupTag;
}
private static void addBookmarkItemToPayload(BookmarkPayload payload, BookmarkItem item,
Map<RecipeId, Integer> recipesIndexes) {
final JsonObject itemTag = item.toJsonObject();
itemTag.remove("groupId");
if (item.recipeId != null) {
final int recipeIndex = recipesIndexes
.computeIfAbsent(item.recipeId, recipeId -> addRecipeToPayload(payload, recipeId, recipesIndexes));
itemTag.addProperty("recipeId", recipeIndex);
}
payload.items.appendTag(NBTJson.toNbt(itemTag));
}
private static int addRecipeToPayload(BookmarkPayload payload, RecipeId recipeId,
Map<RecipeId, Integer> recipesIndexes) {
final NBTTagCompound recipeTag = new NBTTagCompound();
final int recipeIndex = payload.recipes.tagCount();
recipeTag.setTag("recipeId", NBTJson.toNbt(recipeId.toJsonObject()));
recipeTag.setInteger("recipeIndex", recipeIndex);
payload.recipes.appendTag(recipeTag);
return recipeIndex;
}
public NBTTagCompound toNBT() {
final NBTTagCompound payloadTag = new NBTTagCompound();
payloadTag.setString("linkType", linkType);
if (target != null) {
payloadTag.setTag("item", target);
}
if (group != null) {
payloadTag.setTag("group", group);
}
payloadTag.setTag("items", items);
payloadTag.setTag("recipes", recipes);
return payloadTag;
}
public String getLinkType() {
return this.linkType;
}
public ItemStack getTargetStack() {
return this.target != null ? StackInfo.loadFromNBT(this.target) : null;
}
public BookmarkGroup getGroup() {
if (this.group == null) {
return null;
}
final boolean crafting = this.group.hasKey("crafting") && this.group.getBoolean("crafting");
final BookmarkViewMode viewMode;
if (this.group.hasKey("viewmode")) {
viewMode = BookmarkViewMode.valueOf(this.group.getString("viewmode"));
} else {
viewMode = BookmarkViewMode.TODO_LIST;
}
final BookmarkGroup group = new BookmarkGroup(viewMode, crafting);
if (this.group.hasKey("collapsed")) {
group.collapsed = this.group.getBoolean("collapsed");
}
if (this.group.hasKey("collapsedRecipes")) {
final NBTTagList collapsedRecipes = this.group.getTagList("collapsedRecipes", 10);
for (int i = 0; i < collapsedRecipes.tagCount(); i++) {
final NBTTagCompound recipeTag = collapsedRecipes.getCompoundTagAt(i);
final RecipeId recipeId = RecipeId.of((JsonObject) NBTJson.toJsonObject(recipeTag));
group.collapsedRecipes.add(recipeId);
}
}
return group;
}
public List<BookmarkItem> getBookmarkItems(int groupId) {
final List<BookmarkItem> bookmarkItems = new ArrayList<>();
final Map<Integer, RecipeId> recipesMap = new HashMap<>();
for (int i = 0; i < recipes.tagCount(); i++) {
final NBTTagCompound recipeTag = recipes.getCompoundTagAt(i);
final RecipeId recipeId = RecipeId
.of((JsonObject) NBTJson.toJsonObject(recipeTag.getCompoundTag("recipeId")));
recipesMap.put(recipeTag.getInteger("recipeIndex"), recipeId);
}
for (int i = 0; i < items.tagCount(); i++) {
final NBTTagCompound itemTag = items.getCompoundTagAt(i);
RecipeId recipeId = null;
if (itemTag.hasKey("recipeId")) {
recipeId = recipesMap.get(itemTag.getInteger("recipeId"));
itemTag.removeTag("recipeId");
}
final BookmarkItem item = BookmarkItem.of((JsonObject) NBTJson.toJsonObject(itemTag));
if (item != null) {
item.groupId = groupId;
item.recipeId = recipeId;
bookmarkItems.add(item);
}
}
return bookmarkItems;
}
}
Loaded 100 of 283 files, more files were not shown because too many files have changed in this diff. Show more