Compare commits

...
406 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
slprime b80948f2f2 Fix Subset Name (#705) 2025-08-31 18:34:19 +02:00
slprime 28539bbacc Fix Keyboard IndexOutOfBoundsException (#704) 2025-08-31 18:33:59 +02:00
UltraProdigy ec28bdf116 Make EFR Lingering Potion Recipes Visible in NEI (#703) 2025-08-30 09:10:05 -04:00
sbwaz e5d55a9bd8 Advanced search expressions and improved recipe search in a new pattern mode: extended+ (#692) 2025-08-24 14:35:59 +07:00
slprime c1f7489de1 Presets: Custom Subsets Mode (#671) 2025-08-22 12:22:09 +00:00
slprime fa6fd01a3a Add Option to Disable Hotkeys Tooltip (#680) 2025-08-22 12:15:23 +00:00
slprime 453ba6667f Add Favorite Hotkey (#681) 2025-08-22 19:12:42 +07:00
slprime 4e11a45852 Add the ability to create a chain of items with zero (#682) 2025-08-22 12:06:27 +00:00
slprime 95274339a0 Change panel position algorithm (#683) 2025-08-22 12:03:05 +00:00
slprime 31fcd6af51 Crate Craftables Panel (#696) 2025-08-22 11:58:41 +00:00
koolkrafter5 860bc059ca Allow shift+scrolling other stacks in recipes (#698) 2025-08-18 19:57:08 +07:00
koolkrafter5 2fc2152809 Strict Item Identify Format (#697) 2025-08-16 21:11:27 +07:00
slprimeandkoolkrafter5 57e865fc6f Custom Subsets in config; RecipeId Command (#691)
Co-authored-by: koolkrafter5 <creepersrgreen@gmail.com>
2025-08-16 18:56:59 +07:00
Dream Master 6dcd26a978 update 2025-08-16 18:39:01 +07:00
koolkrafter5 aeb59081d9 Information Handler (#695) 2025-08-16 18:24:40 +07:00
slprime ef8706ae38 Loading items after the player to ensure the hint cache works correctly in some mods (#687) 2025-08-16 07:38:24 +07:00
Dream Master b49d066231 update 2025-08-08 19:05:06 +07:00
slprime 594fbe6f79 Allowed fill crafting grid without all ingredients (#693) 2025-08-06 13:40:52 -04:00
koolkrafter5andEthryan 3edb85b317 Allow handlers to hide the favorite and overlay buttons via IMC (#678)
Co-authored-by: Ethryan <3237986+Ethryan@users.noreply.github.com>
2025-07-29 20:37:37 +07:00
TotallyNotOndre 5fd0a74363 Add ability to bookmark linked items (#674) 2025-07-26 07:42:34 +07:00
Dream Master 2ae94e94a0 update 2025-07-17 22:31:09 +07:00
TotallyNotOndre fa3adde3e6 Add ability to link Item in chat (#673) 2025-07-17 10:09:43 +07:00
slprime 56941cc45d Correctly used tools in favorites (#670) 2025-07-12 07:44:34 +02:00
slprimeandCaedis cd7d014704 Bookmarks Chains: Use active Item as preferred if exists (#664)
Co-authored-by: Caedis <Caedis@users.noreply.github.com>
2025-07-11 14:36:10 +00:00
slprime 0634466851 Bookmarks Chains: Optimize GTTools (#667) 2025-07-11 14:33:14 +00:00
slprimeandCaedis b87e389132 Bookmarks Hotkeys: optimization thaumcraft recipes (#663)
Co-authored-by: Caedis <Caedis@users.noreply.github.com>
2025-07-11 14:25:15 +00:00
slprimeandCaedis c788a07cf5 Bookmarks Chains Math: fix infinity loop (#662)
Co-authored-by: Caedis <Caedis@users.noreply.github.com>
2025-07-11 16:21:32 +02:00
Martin Robertz 7d4ed91e86 update 2025-07-09 14:43:40 +02:00
slprime ca79cdd3c9 Disable play sound when crafting with gt tools (#659) 2025-07-08 15:41:42 +02:00
slprime 3a0722adc2 Update Bookmark Chain Tooltip Cache (#657) 2025-07-01 05:52:46 -05:00
slprime 77fa3bafd6 Autocrafting Improvement (#655) 2025-06-27 08:32:54 -05:00
Martin Robertz e23e542aac update 2025-06-22 18:27:06 +02:00
slprime 7ec2ad132e Tools in autocrafting (#653) 2025-06-22 17:49:20 +02:00
slprime 7b43826d05 Clear Crafting Grid after autocrafting (#652) 2025-06-20 09:25:02 -05:00
slprime c7a18dd288 Fix Item Tooltip (#651) 2025-06-19 16:22:12 +00:00
slprime 4fb1d7b1d8 Add Autocrafting Option (#649) 2025-06-16 23:30:10 +02:00
AlexdoruandMartin Robertz 081071562f Fix WorldClient leak in ItemModSpawner and ClientHandler (#637)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-06-15 12:09:55 +02:00
Martin Robertz 81e05dc030 update 2025-06-14 00:08:03 +02:00
slprime efc54fb662 Add Translation to Item Count Details (#647) 2025-06-13 23:46:32 +02:00
slprime 6e75124ea2 Set Minimum Zoom Size (#648) 2025-06-13 23:46:22 +02:00
slprime 2c9f17983a Fix Paged Tooltip with Line Handler (#645) 2025-06-13 23:46:05 +02:00
slprime e0b6d62143 Add Bookmark items with recipe Option (#644) 2025-06-13 23:45:50 +02:00
Martin Robertz 3d5e9aecf0 update 2025-06-08 11:59:07 +02:00
FalsePattern 07cec30f3e allow setOverrideName to remove name overrides (#643) 2025-06-06 14:11:33 +00:00
koolkrafter5 f3add01e74 Too Many (Hidden) Items (#641) 2025-06-03 08:26:44 +02:00
Maya d469fdc90d update 2025-06-01 18:05:04 -06:00
abel1502 763d9b8cd4 Fix Incorrect handleDragNDrop Implementation (#639) 2025-06-01 18:04:55 -06:00
Jason Mitchell 55ccb70c8b Update JarJar detection so it works when jarjar is loaded but rfb is not setup properly. (#636) 2025-05-26 16:49:52 -04:00
2e1c7764b7 Ignore prefix when SearchMode is ALWAYS (#635)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
2025-05-26 15:55:37 +00:00
Martin Robertz 9daa9cabb2 update 2025-05-26 08:46:40 +02:00
Jason Mitchell 74dc7c34d9 Don't exclude locals/frames when transforming. (#632) 2025-05-18 17:50:06 -05:00
slprimeandslprime e5f85e32ec Add Settings: Dynamic Font Size (#631)
Co-authored-by: slprime <history-21@yandex.ru>
2025-05-18 11:18:12 -05:00
Maya 5383e0891a update 2025-05-13 19:14:13 -05:00
slprimeandslprime 6142d4cc6d Subset: Memory Optimization (#626)
Co-authored-by: slprime <history-21@yandex.ru>
2025-05-13 19:13:17 -05:00
Maya 6aa6b2e6d2 update 2025-05-12 09:46:11 -05:00
koolkrafter5andMaya 29ade6f327 Make the "Accepts following:" tooltip work for otherStacks (#625)
Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
2025-05-12 14:45:57 +00:00
slprimeandslprime 5ad6095c43 Filtering the input in the ItemQuantityField (#623)
Co-authored-by: slprime <history-21@yandex.ru>
2025-05-12 09:40:42 -05:00
Martin Robertz 8ae3304861 update 2025-04-25 13:38:04 +02:00
0dc7bc1228 Create Favorites Widget; More hotkeys; More Tooltips; (#617)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
2025-04-24 17:48:40 +02:00
Maya 19d8b42993 update 2025-04-08 23:53:43 -05:00
Kogepan229 3b7eae67af Add AE2 Cell View handlers (#621) 2025-04-08 23:51:52 -05:00
Glease 64a747e4bd prevent empty pages from being produced (#615) 2025-03-25 15:06:45 +01:00
Martin Robertz 4f60928b61 update 2025-03-24 16:05:00 +01:00
8dba2ff4c1 Add option to adjust FPS rendering of items (#612)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-03-24 15:01:41 +00:00
eb40e3b112 Add Hotkeys Widget Settings (#613)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2025-03-24 15:48:11 +01:00
Martin Robertz 98c8981f05 update 2025-03-19 16:45:47 +01:00
Spacebuilder2020 1ed35c8357 Implement Forge Essentials Permission Checking (#611) 2025-03-18 08:15:30 -07:00
Martin Robertz 6249bab886 update 2025-03-14 19:04:03 +01:00
azodandThéo Jacquelin b525760dd3 feature: Overlays Settings GUI (Keep overlays through dimensions) (#610)
Signed-off-by: AzodFR <thjacque@student.42lyon.fr>
Co-authored-by: Théo Jacquelin <theo.jacquelin@ovhcloud.com>
2025-03-13 17:48:25 +00:00
Jason Mitchell 83b4855f5e Use the updated ASMDataTable (with interfaces) when JarJar is present (#608) 2025-03-13 07:04:41 -07:00
Maya 75352caf11 update 2025-02-08 12:14:10 -06:00
475f5811f6 Add loadPluginsInParallel Option (#604)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
2025-02-08 18:11:17 +00:00
slprimeandslprime 7503e1dfcb Fix NC marker in recipe after fluid pattern terminal (#603)
Co-authored-by: slprime <history-21@yandex.ru>
2025-02-08 12:08:36 -06:00
Martin Robertz 9d1f475941 update 2025-01-27 08:06:08 +01:00
slprimeandslprime ac50d1b27e Added hotkey utils methods (#599)
Co-authored-by: slprime <history-21@yandex.ru>
2025-01-26 12:59:16 -06:00
Maya 48934d621f update 2025-01-25 13:17:03 -06:00
slprimeandslprime 0bbb889f39 Change Permutation Filter (#598)
Co-authored-by: slprime <history-21@yandex.ru>
2025-01-25 13:16:28 -06:00
Martin Robertz af55a6b191 update
(cherry picked from commit bd7e8d05fa)
2025-01-21 17:55:50 +01:00
slprimeandslprime b3e1f23683 Restore Issue 583 (#593)
Co-authored-by: slprime <history-21@yandex.ru>
2025-01-21 10:50:16 -06:00
slprimeandslprime 39578a4419 Update Render Cache after window resize (#592)
Co-authored-by: slprime <history-21@yandex.ru>
2025-01-19 11:33:26 -06:00
e3574800c0 Subset Widget Improvement (#577)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: Caedis <Caedis@users.noreply.github.com>
Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
2025-01-16 21:12:26 +01:00
Martin Robertz 5cd7556e6a update 2025-01-16 12:44:42 +01:00
80c2cba778 Created Grid Rendering Cache Mode (#573)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: Caedis <Caedis@users.noreply.github.com>
2025-01-15 23:23:16 -06:00
Glease 31173a1fbd fix memory leak fixes not working for MP (#590)
#258 addresses the memory leak in ItemMobSpawner. Unfortunately the clean up handler is not consistently called on every world unloaded. This PR fixes this issue by moving the call out of that if block
2025-01-10 17:11:52 +08:00
Caedis fd08a93554 Fix NPE in ItemsTooltipLineHandler (#589) 2025-01-10 10:42:23 +02:00
Martin Robertz 4dace1f6e5 update 2025-01-09 21:05:10 +01:00
0c0cf37eb0 Add 'Hide Item List Until Searching' option (#572)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
2025-01-09 07:53:27 +00:00
Chris Stone 3ec4f8dd68 Add mod handlers for mods in Per Fabrica Ad Astra modpack (#587) 2025-01-08 12:30:34 -06:00
lailani-fandlailani-f e0dd6847cb Item List Sort: Ensure access to shared private field is atomic (#584)
Co-authored-by: lailani-f <>
2025-01-04 14:03:53 +00:00
slprimeandslprime 6374c162ba Fixed NEI reload when changing dimension (#583)
Co-authored-by: slprime <history-21@yandex.ru>
2024-12-31 19:33:21 +01:00
8dd36f1c4a Add RenderTooltipEvent Compat (#574)
Co-authored-by: Martin Robertz <dream-master@gmx.net>
Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
2024-12-28 23:00:02 -06:00
4613ade468 Change OptionIntegerField Width (#575)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
2024-12-29 02:36:53 +00:00
ca7030df72 Add Space Mode Settings to Search (#571)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: boubou19 <miisterunknown@gmail.com>
2024-12-23 10:21:28 +00:00
Martin Robertz b32f25c2ce update 2024-12-22 11:38:38 +01:00
4be7a30578 Fix Accept Folowing for custom recipe handlers (#570)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2024-12-22 03:01:44 +01:00
Martin Robertz 7ac3e32635 update 2024-12-17 20:36:35 +01:00
Martin Robertz 088d37a11a update 2024-12-17 20:36:29 +01:00
slprime 6a787dd5b7 Clear Search Filter Cache after Change Settings (#569) 2024-12-17 13:15:41 -06:00
c305649b95 Optimize TooltipItemFilter; ItemFilter limited cache (#565)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2024-12-07 15:29:09 +01:00
slprimeandslprime b5d2815eab Fix CME in registerRectsToGuis during plugin loading (#566)
Co-authored-by: slprime <history-21@yandex.ru>
2024-12-07 10:32:00 +01:00
slprimeandslprime 6049d0e9c3 Fix restoring collapsible items state (#564)
Co-authored-by: slprime <history-21@yandex.ru>
2024-12-05 20:25:17 +01:00
9288fc4575 Fix Zoom Position in Right Panel (#559)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Ethryan <3237986+Ethryan@users.noreply.github.com>
2024-12-03 06:52:51 +01:00
Alexdoru ec749ecc8e Fix mob spawners spawning mobs at world 0 0 0 (#563) 2024-12-03 02:39:34 +01:00
Alexdoru 7471a82bca fix the handlerordering.csv being empty when created (#561) 2024-12-01 16:56:02 +01:00
kstvr32 3f3c1791cb Fix handler ordering config (#560) 2024-11-29 20:28:13 +00:00
a0e16835c2 Fix Presets Scroll; Fix Placeholder position in the quantity field; Hide rendering error in chat; (#555)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2024-11-17 16:31:10 +01:00
Martin Robertz e9ce5cf72b update 2024-11-16 15:32:06 +01:00
slprimeandslprime fac011b67e Fix Zoom Position (#554)
Co-authored-by: slprime <history-21@yandex.ru>
2024-11-15 10:20:32 +01:00
Worive f9c8ac504e Fix typo for cycledIngredientsTooltip.false in English(#553) 2024-11-14 14:27:14 +00:00
Maya f97711a33a add issue templates 2024-11-11 04:43:49 -06:00
kurrycat b2934c4910 replace GL immediate draw with Tessellator calls for chunk bounds rendering (#549) 2024-10-22 09:29:00 +02:00
Dream Master 759ea69c9f update 2024-10-13 22:01:17 +02:00
36a2cafdac Small Fixes (#543)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2024-10-13 22:00:06 +02:00
Maya 6100012f5b Add Helioflare Power Forge (Gorge) to smelting catalysts (#544) 2024-10-13 21:48:11 +02:00
slprimeandslprime 2e5781beb5 fix catalysts in multiblock structure recipes when shown bookmarks recipe tooltip (#542)
Co-authored-by: slprime <history-21@yandex.ru>
2024-10-06 12:10:05 +02:00
Alexdoru 43918f3bb1 give names to NEI threads (#540) 2024-10-04 21:46:29 +02:00
AtHisF1nestandMarcin Siekierski a41299dfca fix issue #17509 (#539)
Co-authored-by: Marcin Siekierski <marcin.siekierski97@gmail.com>
2024-10-02 17:21:50 +02:00
slprimeandslprime ee060b9253 Small Fixes (#536)
* Small Fixes

* fix shift+v

* fix subset zindex

---------

Co-authored-by: slprime <history-21@yandex.ru>
2024-09-28 18:05:06 +02:00
Jon 82a2273995 Update en_US.lang (#534) 2024-09-15 22:10:18 +02:00
06fadcc362 Try fix Item Render & GuiRecipeHandler (#528)
Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2024-09-15 16:57:55 +02:00
slprimeandslprime b328fd3db1 Fix DraggedStack in Bookmarks (#527)
Co-authored-by: slprime <history-21@yandex.ru>
2024-09-12 18:14:00 +02:00
Martin Robertz 9563ebfe5a update 2024-09-07 18:18:02 +02:00
slprimeandslprime d9143c584e Rename GT Classes; Correct show Meta Keys (#525)
Co-authored-by: slprime <history-21@yandex.ru>
2024-09-07 14:49:46 +02:00
slprimeandslprime 65652e5896 Small fixes & changes (#523)
* fix hotkeys

* code cleanup

* fix recipe tooltip overlay

* search item by original name

* overlay button refactoring

* fix subset filter

* change keyname; issue 524

* cleanup code

* move overlayformat logic

---------

Co-authored-by: slprime <history-21@yandex.ru>
2024-09-01 22:19:12 +02:00
slprimeandslprime bd5e991a84 NEI Optimization (#519)
* NEI Optimization

* save setting after death

---------

Co-authored-by: slprime <history-21@yandex.ru>
2024-08-29 22:14:52 +02:00
glowredman 165afa2910 Small Cleanup (#518)
* Deobfuscate parameters

* Fix generics

* Replace deprecated methods

* Add `serialVersionUID`s

* Fix static access

* Add default switch case

* Close OutputStreams

* Fix wrong variable usage

* Explicitly declare exceptions
2024-08-29 20:07:14 +02:00
glowredman 2507634196 Allow any Character to be used as Search-Prefix (#522)
* Allow any character to be used as prefix

* Enable Jabel

* Add back search mode settings and add ingame setting for prefixes

* Implement prefix redefinitions
2024-08-29 08:17:00 +02:00
slprimeandslprime e0171adc65 Show Delete Item Tooltip only in creative (#517)
Co-authored-by: slprime <history-21@yandex.ru>
2024-08-18 09:27:57 +02:00
72ad8aa2e7 More QOL (#510)
* ItemZoom, hotkeys & more tooltips

* fix tooltips

* fix hover crash to unnamed item

* fix bookmark group with groupIds bigger than 127

---------

Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2024-08-17 22:24:27 +02:00
Dream Master f0a554bcc1 update 2024-08-08 12:02:41 +07:00
cutiegin 65781a1649 Update ru_RU.lang (#512)
* Update ru_RU.lang

* Update ru_RU.lang
2024-08-08 11:50:46 +07:00
Dream Master 6ad8f05323 update 2024-07-26 22:48:08 +07:00
slprimeandslprime d61a545d9d Collapsibleitems (#506)
* Create Collapsible Items; Presets refactoring

* change lang

* fix preset settings

* fix .top

* fix tooltip

---------

Co-authored-by: slprime <history-21@yandex.ru>
2024-07-26 22:44:54 +07:00
chochem 1774685af6 improve advanced search defaults (#498) 2024-07-26 22:42:42 +07:00
slprimeandslprime a63a227c3c Add Create Groups Method to Bookmarks (#497)
* Add Create Groups Method to Bookmarks

* fix dnd with empty handleName

---------

Co-authored-by: slprime <history-21@yandex.ru>
2024-07-26 07:41:15 +07:00
Dream Master 0f115d5439 update 2024-07-08 13:04:31 +07:00
Jason Mitchell 0fad93fb3b Update CCL Dep (#501) 2024-07-03 09:41:26 -07:00
b1a34c761b Create JEI-like Advanced Search (#485)
* Create JEI like Search

* add subfilter

* fix extended search pattern

* fix search crash without prefixes

* fix unicode case in search

* add regexp to extended patternMode

* move autofocus config to seatch widget panel

* code refactoring

* add matching language to searh parser

* fix type

* fix modname search

* clean cache after add new provider

---------

Co-authored-by: slprime <history-21@yandex.ru>
Co-authored-by: Martin Robertz <dream-master@gmx.net>
2024-06-27 17:03:50 +02:00
Martin Robertz cc9715d9f2 update 2024-06-18 20:33:08 +02:00
Jason Mitchell be253a00d2 Broaden visibility (#495)
Fixes #473
2024-06-18 20:25:26 +02:00
OvermindDL1 5dde3b2e2f More inventory interaction enhancements - autofocus-quickclose (#493)
* Added option (default on because it just feels so much better) to instantly close the inventory when it was just opened with it when pressing the inventory close key is the first input the screen receives, letting you flash open and close it again as is common when inspecting inventory

* Forgot to add the vanilla survival inventory to the autofocus allowlist defaults (should this be hardcoded instead?)  Added missing loadEnableAutoFocus() call.

* Removed logging entry, added a new autofocus default that tests successful (and it overwrites the vanilla inventory gui so needed anyway for it), shortened config comment.
2024-06-11 20:18:49 +02:00
OvermindDL1andMartin Robertz 8da541460f Added ability to have the NEI search be auto-focused on open. (#492)
* Added open to have the NEI search be auto-focused on open.  It will automatically blue if the mouse is moved before typing anything so immediate 'r' and such use works as expected.

* Added open to have the NEI search be auto-focused on open.  It will automatically blue if the mouse is moved before typing anything so immediate 'r' and such use works as expected.

* Add an allowlist for what classnames to allow the search auto-focus to work on

* sa

* Add `INEIAutoFocusSearchEnable` interface for opting in to autofocus search box by a mod.  Moved the autofocus list from config into a standalone file at the loss of live-updating.

* Remove now-superfluous lang entries for search focus prefix allowlist

* Undo intellij automatic star-imports...

---------

Co-authored-by: Martin Robertz <dream-master@gmx.net>
2024-06-10 22:19:25 +02:00
Lasm Gratel dc4426d9c9 fix #479 (#483) 2024-05-27 14:25:19 +02:00
Martin Robertz d05a9eafb3 update 2024-05-04 13:54:08 +02:00
Lasm Gratel 5056fe5437 Concurrent recipe profiler (#478)
* concurrent recipe profiler

* eliminate NPE by giving nulls a name
2024-05-04 13:50:56 +02:00
slprimeandslprime 9fd9aad357 Fix Bookmarks DnD (#476)
Co-authored-by: slprime <history-21@yandex.ru>
2024-04-24 21:25:46 +02:00
Martin Robertz acaead41ee update 2024-04-22 12:51:44 +02:00
D-Cysteine 951f1b0d0d Add Railcraft NEI handlers to height hack (#475) 2024-04-21 21:52:54 +02:00
Martin Robertz daab313288 update 2024-03-21 20:35:30 +01:00
MalTeeez 25eaa492f7 Update icon (#471) 2024-03-21 20:32:17 +01:00
Caedis 1b34b24495 Adjust readability and padding of item panel footer size (#470) 2024-03-07 05:32:08 -06:00
Martin Robertz b9cd48f8f9 Update settings.gradle (#469) 2024-03-07 11:08:51 +01:00
Caedis b2afc8c9e0 Fix history being hidden when search is centered and item quantity bar is disabled (#468) 2024-03-07 11:01:02 +01:00
Martin Robertz dc400ea560 update 2024-03-02 00:30:18 +01:00
slprimeandslprime ea03cfa17d Correctly find the recipeId under mouse when using the search (#465)
* Correctly find the recipeId under mouse when using the search

* optimize

---------

Co-authored-by: slprime <history-21@yandex.ru>
2024-03-01 19:36:15 +01:00
317 changed files with 41140 additions and 9979 deletions

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
name: Report Bug
description: "Report a bug."
body:
- type: markdown
attributes:
value: "A bug/crash report with sufficient information and logs to reproduce and track down."
- type: input
id: discord
attributes:
label: Your GTNH Discord Username
description: Leave empty if you don't have one, but this will make it harder to contact you if we need additional info.
placeholder: "Example: Fake#1234"
- type: input
id: version
attributes:
label: Mod Version
description: "What version of the Mod are you using?"
placeholder: "Example: 1.0.0"
validations:
required: true
- type: textarea
id: report
attributes:
label: Bug Report
description: "Relevant information, as well as relevant logs attached such as `logs/fml-client-latest.log."
placeholder: "Example: https://mclo.gs/ OR submit the file to github by dragging it to this textbox."
validations:
required: true
- type: dropdown
id: java
attributes:
label: Java Version
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 17
- Java 21
- java 25
- java 26
- Other (Please Specify)
validations:
required: true
- type: textarea
id: modlist
attributes:
label: Mod List or GTNH Pack Version
description: "List of mods, ideally a minimal reproducible set (can be retrieved from latest.log). If using GTNH please indicate pack version and any changed mods, not the entire modlist."
placeholder: "List of mods or GTNH Pack version goes here"
validations:
required: true
- type: checkboxes
id: final
attributes:
label: Final Checklist
description: Certify that you read things
options:
- label: "I have searched the issues and haven't found a similar issue."
required: true
@@ -0,0 +1,28 @@
name: Feature Request
description: "Request a feature."
body:
- type: markdown
attributes:
value: "Please use this form to request features. We ask you check if it was already requested though"
- type: input
id: discord
attributes:
label: Your GTNH Discord Username
description: Leave empty if you don't have one, but this will make it harder to contact you if we need additional info.
placeholder: "Example: Wumpus#1234"
- type: textarea
id: request
attributes:
label: Feature Request
description: "Relevant information, as well as relevant images"
placeholder: "Example: https://mclo.gs/ OR submit the file to github by dragging it to this textbox."
validations:
required: true
- type: checkboxes
id: final
attributes:
label: Final Checklist
description: Certify that you read things
options:
- label: "I have searched the issues and haven't found a similar issue."
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
+55
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.
@@ -22,6 +24,59 @@ If you have issues with NEI outside of the GTNH modpack you may report them in t
* Cycle between Recipe, Utility, and Cheat mode by ctrl clicking on the Wrench Icon
* GT5u Tools/Items and GT6 tools should now properly work with the Overlay Recipe Transfer
### Information Page Handler
Want to add some information about a block or item without making a massive tooltip for it? You can add information about any block or item by registering it in the Information Handler.
Information page(s) are displayed when either the uses or recipes of a matching item are searched. All matching items will cycle at the top of the information page.
Your mod can call:
```java
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", nbt);
```
Where nbt is an NBTTagCompound formatted as described below.
| Tag | Type | Description |
|------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `filter` | String | A string filter to match what items should return this information page.<br/>modname:itemname, metadata, Ore Dictionary tags, and regex can all be used. The full format may be seen in `config/NEI/collapsibleitems.cfg`. |
| `page` / `pages` | String / String NBTTagList | The actual text to display. See below for differences between single and multi-page messages.<br/>Both formats will automatically translate provided strings. |
#### Single Page Format
Use the `"page"` string tag when you only want to add one page of information:
```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);
```
#### Multiple Page Format
Use the `"pages"` tag as an `NBTTagList` of strings when you want more than one page:
```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);
```
* Each list entry is one page.
* Pages will appear in NEI with buttons to scroll through them.
#### Item Matching
All items that match `filter` will show your information page. See `config/NEI/collapsibleitems.cfg` for the full filter format.
#### Item Information Config File
If you're a modpack creator and want to make custom item information pages, they can easily be added in `config/NEI/informationpages.cfg`, which has a few more examples.
## Other items of note:
* Remove TMI style
+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.
+26
View File
@@ -1,3 +1,29 @@
compileJava {
options.encoding = "UTF-8"
}
generateGrammarSource {
maxHeapSize = "128m"
arguments += ['-package', 'codechicken.nei.search', '-visitor', '-no-listener']
outputDirectory = file("${projectDir}/build/generated/sources/search/antlr/codechicken/nei/search/")
}
compileJava.dependsOn generateGrammarSource
sourcesJar.dependsOn generateGrammarSource
sourcesJar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
sourceSets {
main {
java.srcDirs += ["build/generated/sources/search/antlr/"]
}
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
+1
View File
@@ -1,5 +1,6 @@
//version: 1707058017
plugins {
id 'antlr'
id 'com.gtnewhorizons.gtnhconvention'
}
+37 -2
View File
@@ -1,7 +1,42 @@
// Add your dependencies here
configurations.create('gtnhlibEmbed') {
canBeConsumed = false
canBeResolved = true
}
dependencies {
api('com.github.GTNewHorizons:CodeChickenLib:1.2.1:dev')
api('com.github.GTNewHorizons:CodeChickenCore:1.2.1: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')
testImplementation(platform('org.junit:junit-bom:5.13.4'))
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation("org.mockito:mockito-core:5.+")
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'
}
}
}
+61 -10
View File
@@ -14,7 +14,7 @@ modId = NotEnoughItems
modGroup = codechicken.nei
# Whether to use modGroup as the maven publishing group.
# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods.
# When false, com.github.GTNewHorizons is used.
useModGroupForPublishing = false
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
@@ -39,13 +39,37 @@ 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 = false
# 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.
enableGenericInjection = false
enableGenericInjection = true
# Generate a class with a String field for the mod version named as defined below.
# If generateGradleTokenClass is empty or not missing, no such class will be generated.
@@ -85,13 +109,20 @@ accessTransformersFile = nei_at.cfg
# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
usesMixins = false
# Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main.
# This can speed up compile times thanks to not running the mixin annotation processor on all input sources.
# Mixin classes will have access to "main" classes, but not the other way around.
separateMixinSourceSet =
# Adds some debug arguments like verbose output and class export.
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!
@@ -117,9 +148,15 @@ minimizeShadowedDependencies = true
# If disabled, won't rename the shadowed classes.
relocateShadowedDependencies = true
# Adds the GTNH maven, CurseMaven, IC2/Player maven, and some more well-known 1.7.10 repositories.
# Adds CurseMaven, Modrinth, and some more well-known 1.7.10 repositories.
includeWellKnownRepositories = true
# A list of repositories to exclude from the includeWellKnownRepositories setting. Should be a space separated
# list of strings, with the acceptable keys being(case does not matter):
# cursemaven
# modrinth
excludeWellKnownRepositories =
# Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven.
# Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables.
# If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle.
@@ -140,8 +177,8 @@ modrinthProjectId = notenoughitems-unofficial
# type can be one of [project, version],
# and the name is the Modrinth project or version slug/id of the other mod.
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
modrinthRelations = required-project\:codechickenlib-unofficial;required-project\:codechickencore-unofficial
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
modrinthRelations = required-project\:codechickencore-unofficial
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
#
@@ -149,18 +186,29 @@ modrinthRelations = required-project\:codechickenlib-unofficial;required-project
# 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],
# and the name is the CurseForge project slug of the other mod.
# Example: requiredDependency:railcraft;embeddedLibrary:cofhlib;incompatible:buildcraft
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
curseForgeRelations = requiredDependency\:codechickencore-unofficial;requiredDependency\:codechickenlib-unofficial
curseForgeRelations = requiredDependency\:codechickencore-unofficial
# Optional parameter to customize the produced artifacts. Use this to preserve artifact naming when migrating older
# 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.
@@ -190,3 +238,6 @@ curseForgeRelations = requiredDependency\:codechickencore-unofficial;requiredDep
# 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.6-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
+9 -10
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.
@@ -15,10 +15,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -27,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:
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/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/.
@@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -112,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.
@@ -170,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" )
@@ -203,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
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
+14 -24
View File
@@ -13,16 +13,18 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@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=.
@@ -49,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:"=%
@@ -63,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%
+5 -1
View File
@@ -1,4 +1,8 @@
// Add any additional repositiroes for your dependencies here
// Add any additional repositories for your dependencies here
repositories {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.14'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.34'
}
+47
View File
@@ -0,0 +1,47 @@
lexer grammar SearchExpressionLexer;
// Antlr4 generates imports with .*
@header {
// CHECKSTYLE:OFF
import codechicken.nei.SearchTokenParser;
}
@members {
private SearchTokenParser searchParser;
public SearchExpressionLexer(CharStream input, SearchTokenParser searchParser) {
this(input);
this.searchParser = searchParser;
}
}
// Lexer rules
REGEX_LEFT : 'r'? '/' -> pushMode(REGEX) ;
QUOTE_LEFT : '"' -> pushMode(QUOTED) ;
DASH : '-' ;
RECIPE_INGREDIENTS : '<&' | '<' ;
RECIPE_RESULT : '>&' | '>' ;
RECIPE_OTHERS : '^&' | '^' ;
OR : '|' ;
LEFT_BRACKET : '{' ;
RIGHT_BRACKET : '}' ;
NEWLINE_OR_TAB : [\t\r\n] -> skip ;
SPACE : ' ' ;
ESCAPED_SYMBOL : '\\' . ;
PREFIX : CLEAN_SYMBOL {searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
CLEAN_SYMBOL : ~[-<>^{}|/\\ "] ;
mode REGEX;
REGEX_CONTENT : (~[/] | '\\/')+ ;
REGEX_RIGHT : '/' -> popMode ;
mode QUOTED;
QUOTED_CONTENT : (~["] | '\\"')+ ;
QUOTE_RIGHT : '"' -> popMode ;
+73
View File
@@ -0,0 +1,73 @@
parser grammar SearchExpressionParser;
// Antlr4 generates imports with .*
@header {
// CHECKSTYLE:OFF
}
options { tokenVocab=SearchExpressionLexer; }
// Parser rules
// Should only be used for recipes. Thanks to antlr inability to properly
// inherit grammars, this is included where it's not supposed to be
recipeSearchExpression
: recipeClauseExpression*
;
recipeClauseExpression
: ingredientsPrefix=RECIPE_INGREDIENTS searchExpression[0, $ingredientsPrefix.getText().length() > 1]
| resultPrefix=RECIPE_RESULT searchExpression[1, $resultPrefix.getText().length() > 1]
| othersPrefix=RECIPE_OTHERS searchExpression[2, $othersPrefix.getText().length() > 1]
| searchExpression[3, false]
;
// General search expression rules
searchExpression[int type, boolean allRecipe]
: orExpression
;
orExpression
: sequenceExpression (OR sequenceExpression)*
;
sequenceExpression
: (SPACE* unaryExpression SPACE*)+
;
unaryExpression
: LEFT_BRACKET orExpression RIGHT_BRACKET?
| prefixedExpression
| negateExpression
| token['\0']
;
negateExpression
: DASH unaryExpression
;
prefixedExpression
locals [
Character prefix
]
: prefixToken=PREFIX { $prefix = $prefixToken.getText().charAt(0); } token[$prefix]
;
token[Character prefix]
: regex[prefix]
| quoted[prefix]
| plainText
;
regex[Character prefix]
: REGEX_LEFT REGEX_CONTENT REGEX_RIGHT?
;
quoted[Character prefix]
: QUOTE_LEFT QUOTED_CONTENT QUOTE_RIGHT?
;
plainText
: (CLEAN_SYMBOL | ESCAPED_SYMBOL)+
;
@@ -0,0 +1,148 @@
package codechicken.nei;
import net.minecraft.client.gui.inventory.GuiContainer;
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.GuiRecipe;
public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSlot, ? extends ItemsGrid.MouseContext>>
extends Widget implements IContainerTooltipHandler {
final DrawableResource DASH = new DrawableBuilder("nei:textures/dash.png", 0, 0, 6, 1).setTextureSize(6, 1).build();
protected int mouseDownItemIndex = -1;
protected int splittingLineColor = 0xffffffff;
protected int linePaddingStart = 0;
protected T grid;
public boolean isEmpty() {
return this.grid.isEmpty();
}
public void draw(int mousex, int mousey) {
drawSplittingLine();
this.grid.draw(mousex, mousey);
}
public void update() {
this.grid.update();
}
public abstract int setPanelWidth(int width);
public void resize(GuiContainer gui) {
this.grid.setGridSize(x, y + 4, w, h - 8);
this.grid.refresh(gui);
}
@Override
public ItemStack getStackMouseOver(int mousex, int mousey) {
final ItemsGridSlot slot = getSlotMouseOver(mousex, mousey);
return slot == null ? null : slot.getItemStack();
}
public ItemsGridSlot getSlotMouseOver(int mousex, int mousey) {
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 - this.linePaddingStart;
int repeat = width / 6;
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, this.y);
}
GL11.glColor4f(1, 1, 1, 1);
}
@Override
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
if (this.mouseDownItemIndex >= 0 && ItemPanels.itemPanel.draggedStack == null
&& NEIClientUtils.getHeldItem() == null
&& NEIClientConfig.hasSMPCounterPart()) {
final ItemsGridSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
if (mouseOverSlot == null || mouseOverSlot.itemIndex != this.mouseDownItemIndex || heldTime > 500) {
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(
this.grid.getItem(this.mouseDownItemIndex));
this.mouseDownItemIndex = -1;
}
}
}
@Override
public boolean handleClick(int mousex, int mousey, int button) {
if (handleClickExt(mousex, mousey, button)) return true;
final ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (hoverSlot != null) {
if (button == 2) {
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(hoverSlot.getItemStack());
} else {
mouseDownItemIndex = hoverSlot.itemIndex;
}
return true;
}
return false;
}
protected abstract ItemStack getDraggedStackWithQuantity(ItemStack itemStack);
public ItemStack getStackMouseOverWithQuantity(int mousex, int mousey) {
final ItemStack hoverSlot = getStackMouseOver(mousex, mousey);
return hoverSlot != null ? getDraggedStackWithQuantity(hoverSlot) : null;
}
@Override
public void mouseUp(int mousex, int mousey, int button) {
final ItemsGridSlot hoverSlot = getSlotMouseOver(mousex, mousey);
if (hoverSlot != null && hoverSlot.itemIndex == this.mouseDownItemIndex
&& ItemPanels.itemPanel.draggedStack == null) {
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|| !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) {
ShortcutInputHandler.handleMouseClick(hoverSlot.getItemStack().copy());
} else {
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1);
}
}
this.mouseDownItemIndex = -1;
}
}
@@ -0,0 +1,180 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.getMousePosition;
import java.awt.Point;
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;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
public class AutoFocusWidget implements IContainerInputHandler {
/**
* Implement this interface on a GuiContainer to enable the autofocus search in NEI (if the autofocus option is
* enabled) without needing to add the classname to the config file .
*/
public interface INEIAutoFocusSearchEnable {
}
public static AutoFocusWidget instance = new AutoFocusWidget();
public static List<String> enableAutoFocusPrefixes = new ArrayList<>();
protected boolean autofocus = false;
protected Point mouse;
protected Point pendingMouse;
protected GuiTextField textInputHolder;
public AutoFocusWidget() {
GuiContainerManager.addInputHandler(this);
}
public void load(GuiContainer gui) {
this.autofocus = LayoutManager.searchField.isVisible() && NEIClientConfig.searchWidgetAutofocus() != 0
&& isAllowedGuiAutoSearchFocus(gui);
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) {
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 !LayoutManager.searchField.text().equals(oldText);
}
return false;
}
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
@Override
public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
cancel();
return false;
}
@Override
public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
cancel();
return false;
}
@Override
public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) {}
@Override
public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
cancel();
}
@Override
public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) {
cancel();
}
@Override
public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
cancel();
}
@Override
public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) {
cancel();
}
protected boolean isAllowedGuiAutoSearchFocus(GuiContainer gui) {
if (gui instanceof INEIAutoFocusSearchEnable) {
return true;
}
String guiClassName = gui.getClass().getName();
for (String prefix : enableAutoFocusPrefixes) {
if (guiClassName.startsWith(prefix)) {
return true;
}
}
return false;
}
}
@@ -1,315 +0,0 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
import codechicken.nei.BookmarkPanel.ItemStackMetadata;
import codechicken.nei.recipe.BookmarkRecipeId;
import codechicken.nei.recipe.StackInfo;
public class BookmarkCraftingChain {
public HashMap<ItemStack, ItemStack> calculatedItems = new HashMap<>();
public HashMap<ItemStack, Integer> multiplier = new HashMap<>();
public HashMap<ItemStack, ItemStack> inputs = new HashMap<>();
public HashMap<ItemStack, ItemStack> outputs = new HashMap<>();
public HashMap<ItemStack, ItemStack> intermediate = new HashMap<>();
protected static class CraftingChainItem {
public String guid;
public int stackIndex = 0;
public int recipeIndex = 0;
public int factor = 0;
public int count = 0;
public boolean ingredient = false;
public int fluidCellAmount = 1;
public ItemStack stack = null;
public CraftingChainItem(ItemStack stack, ItemStackMetadata stackMetadata) {
FluidStack fluid = StackInfo.getFluid(stack);
this.recipeIndex = getRecipeIndex(stackMetadata.recipeId);
this.ingredient = stackMetadata.ingredient;
this.stack = stack;
if (fluid != null) {
this.stackIndex = getStackIndex(fluid);
this.count = fluid.amount * Math.max(0, stack.stackSize);
this.fluidCellAmount = StackInfo.isFluidContainer(stack) ? fluid.amount : 1;
this.factor = this.fluidCellAmount * stackMetadata.factor;
} else {
this.stackIndex = getStackIndex(stack);
this.count = StackInfo.itemStackToNBT(stack).getInteger("Count");
this.factor = stackMetadata.factor;
}
this.guid = this.stackIndex + " " + this.recipeIndex;
}
public ItemStack getItemStack(long count) {
return StackInfo.loadFromNBT(
StackInfo.itemStackToNBT(this.stack),
this.factor > 0 ? (count / this.fluidCellAmount) : 0);
}
public static void clearStatic() {
itemCache.clear();
fluidCache.clear();
recipeCache.clear();
}
private static HashMap<ItemStack, Integer> itemCache = new HashMap<>();
private static int getStackIndex(ItemStack stackA) {
if (!itemCache.containsKey(stackA)) {
for (ItemStack item : itemCache.keySet()) {
if (StackInfo.equalItemAndNBT(stackA, item, true)) {
itemCache.put(stackA, itemCache.get(item));
return itemCache.get(stackA);
}
}
itemCache.put(stackA, itemCache.size() + fluidCache.size());
}
return itemCache.get(stackA);
}
private static HashMap<FluidStack, Integer> fluidCache = new HashMap<>();
private static int getStackIndex(FluidStack fluidA) {
if (!fluidCache.containsKey(fluidA)) {
for (FluidStack item : fluidCache.keySet()) {
if (fluidA.isFluidEqual(item)) {
fluidCache.put(fluidA, fluidCache.get(item));
return fluidCache.get(fluidA);
}
}
fluidCache.put(fluidA, itemCache.size() + fluidCache.size());
}
return fluidCache.get(fluidA);
}
private static HashMap<BookmarkRecipeId, Integer> recipeCache = new HashMap<>();
private static int getRecipeIndex(BookmarkRecipeId recipe) {
if (recipe == null) {
return -1;
}
if (!recipeCache.containsKey(recipe)) {
for (BookmarkRecipeId item : recipeCache.keySet()) {
if (item.equals(recipe)) {
recipeCache.put(recipe, recipeCache.get(item));
return recipeCache.get(recipe);
}
}
recipeCache.put(recipe, recipeCache.size());
}
return recipeCache.get(recipe);
}
}
protected static class CraftingChainRequest {
public List<CraftingChainItem> items = new ArrayList<>();
public HashMap<Integer, Integer> multiplier = new HashMap<>();
public HashMap<String, Long> counts = new HashMap<>();
public HashMap<Integer, HashSet<CraftingChainItem>> inputs = new HashMap<>();
public HashMap<Integer, HashSet<CraftingChainItem>> outputs = new HashMap<>();
public HashSet<Integer> initialItems = new HashSet<>();
public HashSet<Integer> startedIngrs = new HashSet<>();
public CraftingChainRequest(List<ItemStack> items, List<ItemStackMetadata> metadata) {
HashSet<Integer> inputs = new HashSet<>();
HashSet<Integer> outputs = new HashSet<>();
for (int index = 0; index < items.size(); index++) {
CraftingChainItem item = new CraftingChainItem(items.get(index), metadata.get(index));
item.guid = item.ingredient ? "i" + item.guid : "r" + item.guid;
this.items.add(item);
if (this.multiplier.get(item.recipeIndex) == null) {
this.multiplier.put(item.recipeIndex, 0);
}
this.counts.put(item.guid, this.counts.getOrDefault(item.guid, 0L) + item.count);
if (item.ingredient) {
inputs.add(item.recipeIndex);
if (this.inputs.get(item.stackIndex) == null) {
this.inputs.put(item.stackIndex, new HashSet<>());
}
this.inputs.get(item.stackIndex).add(item);
} else {
outputs.add(item.recipeIndex);
if (this.outputs.get(item.stackIndex) == null) {
this.outputs.put(item.stackIndex, new HashSet<>());
}
this.outputs.get(item.stackIndex).add(item);
}
}
for (int recipeIndex : this.multiplier.keySet()) {
if (recipeIndex == -1 || !inputs.contains(recipeIndex) || !outputs.contains(recipeIndex)) {
this.initialItems.add(recipeIndex);
}
}
for (CraftingChainItem item : this.items) {
if (item.ingredient == false && !this.initialItems.contains(item.recipeIndex)) {
this.startedIngrs.add(item.stackIndex);
}
}
CraftingChainItem.clearStatic();
}
}
public void refresh(List<ItemStack> items, List<ItemStackMetadata> metadata) {
CraftingChainRequest request = new CraftingChainRequest(items, metadata);
HashMap<Integer, Long> iShift = new HashMap<>();
HashMap<Integer, Long> oShift = new HashMap<>();
boolean change = true;
int iteration = 100;
while (--iteration > 0 && change) {
change = false;
for (int stackIndex : request.startedIngrs) {
change = calculateShift(request, stackIndex) || change;
}
}
this.inputs.clear();
this.outputs.clear();
this.intermediate.clear();
this.calculatedItems.clear();
this.multiplier.clear();
for (CraftingChainItem item : request.items) {
if (request.initialItems.contains(item.recipeIndex)) {
long iCount = calculateCount(request, request.inputs.get(item.stackIndex))
- iShift.getOrDefault(item.stackIndex, 0L);
this.calculatedItems.put(item.stack, item.stack);
if (iCount == 0) {
this.inputs.put(item.stack, item.getItemStack(0));
} else {
long count = Math.min(iCount, item.count);
this.inputs.put(item.stack, item.getItemStack(count));
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + count);
}
}
}
for (CraftingChainItem item : request.items) {
if (!request.initialItems.contains(item.recipeIndex)) {
long count = item.count + item.factor * request.multiplier.get(item.recipeIndex);
this.calculatedItems.put(item.stack, item.getItemStack(count));
if (item.ingredient) { // input
long oCount = calculateCount(request, request.outputs.get(item.stackIndex))
- oShift.getOrDefault(item.stackIndex, 0L);
if (oCount < count) {
this.inputs.put(item.stack, item.getItemStack(count - oCount));
oShift.put(item.stackIndex, oShift.getOrDefault(item.stackIndex, 0L) + oCount);
} else if (oCount >= count) {
this.intermediate.put(item.stack, item.getItemStack(0));
oShift.put(item.stackIndex, oShift.getOrDefault(item.stackIndex, 0L) + count);
}
} else { // output
long iCount = calculateCount(request, request.inputs.get(item.stackIndex))
- iShift.getOrDefault(item.stackIndex, 0L);
this.multiplier.put(item.stack, item.factor > 0 ? (int) (count / item.factor) : 0);
if (iCount < count) {
this.outputs.put(item.stack, item.getItemStack(count - iCount));
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + iCount);
} else if (iCount >= count) {
this.intermediate.put(item.stack, item.getItemStack(0));
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + count);
}
}
}
}
}
private boolean calculateShift(CraftingChainRequest request, int stackIndex) {
if (!request.outputs.containsKey(stackIndex)) {
return false;
}
int minRecipeIndex = 0;
int minShift = Integer.MAX_VALUE;
for (CraftingChainItem item : request.outputs.get(stackIndex)) {
if (!request.initialItems.contains(item.recipeIndex) && item.factor > 0) {
long ingrCount = calculateCount(request, request.inputs.get(item.stackIndex), item.recipeIndex);
long outputCount = calculateCount(request, request.outputs.get(item.stackIndex));
long shift = (long) Math.ceil((ingrCount - outputCount) / (double) item.factor);
if (shift > 0 && shift < minShift) {
minShift = (int) shift;
minRecipeIndex = item.recipeIndex;
}
}
}
if (minShift < Integer.MAX_VALUE) {
request.multiplier.put(minRecipeIndex, request.multiplier.get(minRecipeIndex) + minShift);
return true;
}
return false;
}
private long calculateCount(CraftingChainRequest request, HashSet<CraftingChainItem> items) {
return calculateCount(request, items, -1);
}
private long calculateCount(CraftingChainRequest request, HashSet<CraftingChainItem> items, int recipeIndex) {
long count = 0L;
if (items != null) {
for (CraftingChainItem item : items) {
if (item.recipeIndex != recipeIndex || recipeIndex == -1) {
count += request.counts.get(item.guid) + item.factor * request.multiplier.get(item.recipeIndex);
}
}
}
return count;
}
}
File diff suppressed because it is too large. Load diff
+5 -1
View File
@@ -15,7 +15,7 @@ public abstract class Button extends Widget {
}
public int contentWidth() {
return getRenderIcon() == null ? getStringWidth(label) : getRenderIcon().width;
return getRenderIcon() == null ? getStringWidth(getRenderLabel()) : getRenderIcon().width;
}
@Override
@@ -57,6 +57,10 @@ public abstract class Button extends Widget {
return label;
}
public int getRenderLabelColor() {
return 0;
}
public final String label;
public Image icon;
+183 -125
View File
@@ -6,46 +6,57 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.gui.GuiMainMenu;
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;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.IOUtils;
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;
import cpw.mods.fml.client.CustomModLoadingErrorDisplayException;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModClassLoader;
import cpw.mods.fml.common.discovery.ASMDataTable;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
@@ -54,17 +65,9 @@ import cpw.mods.fml.relauncher.SideOnly;
public class ClientHandler {
private static String[] defaultHandlerOrdering = {
"# Each line in this file should either be a comment (starts with '#') or an ordering.",
"# Ordering lines are <handler ID>,<ordering number>.",
"# Handlers will be sorted in order of number ascending, so smaller numbers first.",
"# Any handlers that are missing from this file will be assigned to 0.", "# Negative numbers are fine.",
"# If you delete this file, it will be regenerated with all registered handler IDs.", };
private static ClientHandler instance;
private ArrayList<EntityItem> SMPmagneticItems = new ArrayList<>();
private final ArrayList<EntityItem> SMPmagneticItems = new ArrayList<>();
private World lastworld;
private GuiScreen lastGui;
public void addSMPMagneticItem(int i, World world) {
WorldClient cworld = (WorldClient) world;
@@ -147,91 +150,137 @@ public class ClientHandler {
public static void preInit() {
loadSerialHandlers();
loadHiddenItems();
loadHeightHackHandlers();
loadHiddenHandlers();
loadEnableAutoFocus();
loadGuiCraftablesBlacklist();
loadDefaultBookmarkContainers();
ItemInfo.preInit();
StackInfo.loadGuidFilters();
}
public static void loadSerialHandlers() {
File file = NEIClientConfig.serialHandlersFile;
if (!file.exists()) {
public static void loadSettingsFile(String resource, Consumer<Stream<String>> callback) {
loadSettingsFile(resource, (file, writer) -> {
String folder = resource.substring(resource.lastIndexOf(".") + 1);
URL defaultResource = ClientHandler.class.getResource("/assets/nei/" + folder + "/" + resource);
if (defaultResource != null) {
try {
IOUtils.copy(defaultResource.openStream(), writer);
} catch (IOException e) {}
}
}, callback);
}
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() && createDefault != null) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default serial handlers list {}", file);
URL defaultSerialHandlersResource = ClientHandler.class
.getResource("/assets/nei/cfg/serialhandlers.cfg");
if (defaultSerialHandlersResource != null) {
IOUtils.copy(defaultSerialHandlersResource.openStream(), writer);
}
NEIClientConfig.logger.info("Creating default '{}' {}", resource, file);
createDefault.accept(file, writer);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default serial handlers list to file {}", file, e);
NEIClientConfig.logger.error("Failed to save default '{}' to file {}", resource, file, e);
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading serial handlers from file {}", file);
NEIClientConfig.serialHandlers = IOUtils.readLines(reader).stream().filter((line) -> !line.startsWith("#"))
.collect(Collectors.toCollection(HashSet::new));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load serial handlers from file {}", 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() {
loadSettingsFile(
"serialhandlers.cfg",
lines -> NEIClientConfig.serialHandlers = lines.collect(Collectors.toCollection(HashSet::new)));
}
public static void loadHeightHackHandlers() {
File file = NEIClientConfig.heightHackHandlersFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default height hack handlers list {}", file);
URL defaultHeightHackHandlersResource = ClientHandler.class
.getResource("/assets/nei/cfg/heighthackhandlers.cfg");
if (defaultHeightHackHandlersResource != null) {
IOUtils.copy(defaultHeightHackHandlersResource.openStream(), writer);
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default height hack handlers list to file {}", file, e);
}
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading height hack handlers from file {}", file);
NEIClientConfig.heightHackHandlerRegex = IOUtils.readLines(reader).stream()
.filter((line) -> !line.startsWith("#")).map(Pattern::compile)
.collect(Collectors.toCollection(HashSet::new));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load height hack handlers from file {}", file, e);
}
loadSettingsFile(
"heighthackhandlers.cfg",
lines -> NEIClientConfig.heightHackHandlerRegex = lines.map(Pattern::compile)
.collect(Collectors.toCollection(HashSet::new)));
}
public static void loadHiddenHandlers() {
File file = NEIClientConfig.hiddenHandlersFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default hidden handlers list {}", file);
URL defaultHeightHackHandlersResource = ClientHandler.class
.getResource("/assets/nei/cfg/hiddenhandlers.cfg");
if (defaultHeightHackHandlersResource != null) {
IOUtils.copy(defaultHeightHackHandlersResource.openStream(), writer);
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default hidden handlers list to file {}", file, e);
}
}
loadSettingsFile(
"hiddenhandlers.cfg",
lines -> NEIClientConfig.hiddenHandlers = lines.collect(Collectors.toCollection(HashSet::new)));
}
try (FileReader reader = new FileReader(file)) {
NEIClientConfig.logger.info("Loading hidden handlers from file {}", file);
NEIClientConfig.hiddenHandlers = IOUtils.readLines(reader).stream().filter((line) -> !line.startsWith("#"))
.collect(Collectors.toCollection(HashSet::new));
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load hidden handlers from file {}", file, e);
}
public static void loadEnableAutoFocus() {
loadSettingsFile(
"enableautofocus.cfg",
lines -> AutoFocusWidget.enableAutoFocusPrefixes = lines.collect(Collectors.toList()));
}
public static void loadGuiCraftablesBlacklist() {
loadSettingsFile(
"guicraftablesblacklist.cfg",
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));
}
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();
@@ -239,53 +288,60 @@ public class ClientHandler {
}
public static void postInit() {
loadHandlerOrdering();
GuiContainerManager.registerReloadResourceListener();
}
public static void loadHandlerOrdering() {
File file = NEIClientConfig.handlerOrderingFile;
if (!file.exists()) {
try (FileWriter writer = new FileWriter(file)) {
NEIClientConfig.logger.info("Creating default handler ordering CSV {}", file);
List<String> toWrite = Lists.newArrayList(defaultHandlerOrdering);
GuiRecipeTab.handlerMap.keySet().stream().sorted()
.forEach(handlerId -> toWrite.add(String.format("%s,0", handlerId)));
final String COMMA_DELIMITER = ",";
final String[] defaultHandlerOrdering = {
"# Each line in this file should either be a comment (starts with '#') or an ordering.",
"# Ordering lines are <handler ID>,<ordering number>.",
"# Handlers will be sorted in order of number ascending, so smaller numbers first.",
"# Any handlers that are missing from this file will be assigned to 0.", "# Negative numbers are fine.",
"# If you delete this file, it will be regenerated with all registered handler IDs.", };
loadSettingsFile("handlerordering.csv", (file, writer) -> {
List<String> toWrite = Lists.newArrayList(defaultHandlerOrdering);
GuiRecipeTab.handlerMap.keySet().stream().sorted()
.forEach(handlerId -> toWrite.add(String.format("%s" + COMMA_DELIMITER + "0", handlerId)));
try {
IOUtils.writeLines(toWrite, "\n", writer);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save default handler ordering to file {}", file, e);
}
}
} catch (IOException e) {}
}, lines -> lines.map(line -> line.split(COMMA_DELIMITER)).filter(parts -> parts.length == 2).forEach(parts -> {
String handlerId = parts[0];
int ordering = Integer.parseInt(parts[1]);
NEIClientConfig.handlerOrdering.put(handlerId, ordering);
}));
}
URL url;
try {
url = file.toURI().toURL();
} catch (MalformedURLException e) {
NEIClientConfig.logger.info("Invalid URL for handler ordering CSV.");
e.printStackTrace();
return;
}
public static void loadPluginsList() {
final boolean jarjar = Launch.blackboard.getOrDefault("jarjar.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!jarjar) {
// Do the old style class discovery
final ClassDiscoverer classDiscoverer = new ClassDiscoverer(
test -> test.startsWith("NEI") && test.endsWith("Config.class"),
IConfigureNEI.class);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
NEIClientConfig.logger.info("Loading handler ordering from file {}", file);
CSVParser csvParser = CSVFormat.EXCEL.withCommentMarker('#').parse(reader);
for (CSVRecord record : csvParser) {
final String handlerId = record.get(0);
NEIClientConfig.pluginsList.addAll(classDiscoverer.findClasses());
NEIClientConfig.logger
.info("Loading NEI Plugins via ClassDiscoverer: " + NEIClientConfig.pluginsList.size());
int ordering;
} else {
// If JarJar's loaded, the ASMDataTable now includes interfaces in addition to Annotations, so use that
// instead Includes a small change in behavior of no longer requiring the class to start with NEI and end
// with Config; This could easily be changed if desired, but the previous behavior was likely to speed
// things up by limiting the scope when looking at all of the jars and classes in the classpath.
final ASMDataTable dataTable = NEIModContainer.getAsmDataTable();
final ModClassLoader modClassLoader = (ModClassLoader) Loader.instance().getModClassLoader();
for (ASMDataTable.ASMData asmData : dataTable.getAll(IConfigureNEI.class.getName().replace('.', '/'))) {
final String className = asmData.getClassName().replace('/', '.');
try {
ordering = Integer.parseInt(record.get(1));
} catch (NumberFormatException e) {
NEIClientConfig.logger.error("Error parsing CSV record {}: {}", record, e);
continue;
NEIClientConfig.pluginsList.add(Class.forName(className, true, modClassLoader));
} catch (ClassNotFoundException e) {
NEIClientConfig.logger.error("Failed to load plugin class {}", className, e);
}
NEIClientConfig.handlerOrdering.put(handlerId, ordering);
}
} catch (Exception e) {
NEIClientConfig.logger.info("Error parsing CSV");
e.printStackTrace();
NEIClientConfig.logger.info("Loading NEI Plugins via JarJar: " + NEIClientConfig.pluginsList.size());
}
}
@@ -309,18 +365,20 @@ public class ClientHandler {
if (mc.currentScreen == null) NEIController.processCreativeCycling(mc.thePlayer.inventory);
updateMagnetMode(mc.theWorld, mc.thePlayer);
} else {
lastworld = null;
}
}
GuiScreen gui = mc.currentScreen;
if (gui != lastGui) {
if (gui instanceof GuiMainMenu) lastworld = null;
else if (gui instanceof GuiSelectWorld) NEIClientConfig.reloadSaves();
else if (gui == null) {
/* prevent WorldClient reference being held in the Gui */
NEIController.manager = null;
}
@SubscribeEvent
public void onGuiScreen(GuiOpenEvent event) {
if (event.gui instanceof GuiSelectWorld) {
NEIClientConfig.reloadSaves();
} else if (event.gui == null) {
/* prevent WorldClient reference being held in the Gui */
NEIController.manager = null;
GuiInfo.clearGuiHandlers();
}
lastGui = gui;
}
@SubscribeEvent
@@ -339,24 +397,23 @@ public class ClientHandler {
Minecraft mc = Minecraft.getMinecraft();
if (event.world == mc.theWorld) {
NEIClientConfig.unloadWorld();
ItemMobSpawner.clearEntityReferences();
}
}
public void loadWorld(World world, boolean fromServer) {
if (world != lastworld) {
SMPmagneticItems.clear();
WorldOverlayRenderer.reset();
if (!NEIClientConfig.getBooleanSetting("world.overlays.lock")) {
WorldOverlayRenderer.reset();
}
if (!fromServer) {
NEIClientConfig.setHasSMPCounterPart(false);
NEIClientConfig.setInternalEnabled(false);
if (!Minecraft.getMinecraft().isSingleplayer()) // wait for server to initiate in singleplayer
NEIClientConfig.loadWorld("remote/" + ClientUtils.getServerIP().replace(':', '~'));
ItemMobSpawner.clearEntityReferences(world);
}
lastworld = world;
}
}
@@ -383,9 +440,10 @@ public class ClientHandler {
}
};
@SuppressWarnings("serial")
CustomModLoadingErrorDisplayException e = new CustomModLoadingErrorDisplayException() {
private static final long serialVersionUID = -5593387489666663375L;
@Override
public void initGui(GuiErrorScreen errorScreen, FontRenderer fontRenderer) {
Minecraft.getMinecraft().displayGuiScreen(errorGui);
@@ -0,0 +1,252 @@
package codechicken.nei;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
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;
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.util.ItemStackFilterParser;
public class CollapsibleItems {
protected static class GroupItem {
public String guid;
public ItemFilter filter;
public boolean expanded = false;
public String displayName = "";
public void setFilter(String filter) {
this.filter = ItemStackFilterParser.parse(filter.trim());
this.guid = this.filter != null ? UUID.nameUUIDFromBytes(filter.getBytes()).toString() : "";
}
public void setFilter(ItemFilter filter, String guid) {
this.filter = filter;
this.guid = guid;
}
public boolean matches(ItemStack stack) {
return this.filter.matches(stack);
}
}
private static final String STATE_KEY = "collapsibleitems";
protected static File statesFile;
protected static final List<GroupItem> groups = new ArrayList<>();
protected static final Map<ItemStack, Integer> cache = new ConcurrentHashMap<>();
private CollapsibleItems() {}
public static void load() {
CollapsibleItems.groups.clear();
CollapsibleItems.cache.clear();
for (int i = PresetsList.presets.size() - 1; i >= 0; i--) {
Preset preset = PresetsList.presets.get(i);
if (preset.enabled && preset.mode == PresetMode.GROUP) {
GroupItem group = new GroupItem();
group.setFilter(preset, UUID.nameUUIDFromBytes(preset.items.toString().getBytes()).toString());
group.displayName = preset.name;
addGroup(group);
}
}
if (NEIClientConfig.enableCollapsibleItems()) {
ClientHandler.loadSettingsFile(
"collapsibleitems.cfg",
lines -> parseFile(lines.collect(Collectors.toCollection(ArrayList::new))));
}
loadStates();
}
private static void parseFile(List<String> itemStrings) {
final JsonParser parser = new JsonParser();
GroupItem group = new GroupItem();
for (String itemStr : itemStrings) {
try {
if (itemStr.startsWith("; ")) {
JsonObject settings = parser.parse(itemStr.substring(2)).getAsJsonObject();
if (settings.get("displayName") != null) {
group.displayName = settings.get("displayName").getAsString();
}
if (settings.get("unlocalizedName") != null) {
String unlocalizedName = settings.get("unlocalizedName").getAsString();
String displayName = StatCollector.translateToLocal(unlocalizedName);
if (!displayName.equals(unlocalizedName)) {
group.displayName = displayName;
}
}
if (settings.get("expanded") != null) {
group.expanded = settings.get("expanded").getAsBoolean();
}
} else {
group.setFilter(itemStr);
}
if (group.filter != null) {
addGroup(group);
group = new GroupItem();
}
} catch (IllegalArgumentException | JsonSyntaxException | IllegalStateException e) {
NEIClientConfig.logger.error("Failed to load collapsible items from json string:\n{}", itemStr);
}
}
}
private static void addGroup(GroupItem group) {
if (group == null || group.filter == null
|| group.filter instanceof EverythingItemFilter
|| group.filter instanceof NothingItemFilter)
return;
CollapsibleItems.groups.add(group);
}
public static boolean isEmpty() {
return CollapsibleItems.groups.isEmpty();
}
public static ItemFilter getItemFilter() {
AnyMultiItemFilter filter = new AnyMultiItemFilter();
for (GroupItem group : CollapsibleItems.groups) {
filter.filters.add(group.filter);
}
return filter;
}
public static void clearCache() {
CollapsibleItems.cache.clear();
}
public static void putItem(ItemStack stack) {
int groupIndex = -1;
for (int i = 0; i < CollapsibleItems.groups.size() && groupIndex == -1; i++) {
if (CollapsibleItems.groups.get(i).matches(stack)) {
groupIndex = i;
}
}
if (groupIndex != -1) {
CollapsibleItems.cache.put(stack, groupIndex);
}
}
public static int getGroupIndex(ItemStack stack) {
if (stack == null) {
return -1;
}
return CollapsibleItems.cache.getOrDefault(stack, -1);
}
public static String getDisplayName(int groupIndex) {
if (groupIndex < CollapsibleItems.groups.size()) {
return CollapsibleItems.groups.get(groupIndex).displayName;
}
return null;
}
public static boolean isExpanded(int groupIndex) {
if (groupIndex < CollapsibleItems.groups.size()) {
return CollapsibleItems.groups.get(groupIndex).expanded;
}
return true;
}
public static void setExpanded(int groupIndex, boolean expanded) {
if (groupIndex < CollapsibleItems.groups.size()) {
CollapsibleItems.groups.get(groupIndex).expanded = expanded;
}
}
public static void toggleGroups(Boolean expanded) {
if (expanded == null) {
expanded = CollapsibleItems.groups.stream().noneMatch(g -> g.expanded);
}
for (GroupItem group : CollapsibleItems.groups) {
group.expanded = expanded;
}
}
public static void saveStates() {
NBTTagCompound list = new NBTTagCompound();
for (GroupItem group : CollapsibleItems.groups) {
list.setBoolean(group.guid, group.expanded);
}
if (NEIClientConfig.world != null) {
NEIClientConfig.world.nbt.setTag(STATE_KEY, list);
}
}
private static void loadStates() {
try {
if (NEIClientConfig.world.nbt.hasKey(STATE_KEY)) {
NBTTagCompound states = NEIClientConfig.world.nbt.getCompoundTag(STATE_KEY);
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, NBTBase> nbtEntry : list.entrySet()) {
if (mapping.containsKey(nbtEntry.getKey())) {
mapping.get(nbtEntry.getKey()).expanded = ((NBTPrimitive) nbtEntry.getValue()).func_150290_f()
== 1;
}
}
}
} catch (Exception e) {
NEIClientConfig.logger.error("Error loading collapsible items states", e);
}
}
}
@@ -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++) {
@@ -116,7 +116,6 @@ public class ContainerPotionCreator extends ContainerExtended {
if (!player.worldObj.isRemote) InventoryUtils.dropOnClose(player, potionInv);
}
@SuppressWarnings("unchecked")
@Override
public void handleServerPacket(PacketCustom packet) {
ItemStack potion = potionInv.getStackInSlot(0);
@@ -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) {}
@@ -1,24 +0,0 @@
package codechicken.nei;
import net.minecraft.item.ItemStack;
import codechicken.nei.api.ItemFilter;
public class DoubleFilteredFilter implements ItemFilter {
private ItemFilter filter;
private ItemList.PatternItemFilter patternFilter;
public DoubleFilteredFilter(ItemFilter filter, String itemFilter) {
this.filter = filter;
this.patternFilter = new ItemList.PatternItemFilter(SearchField.getPattern(itemFilter));
}
@Override
public boolean matches(ItemStack item) {
if (filter.matches(item)) {
return patternFilter.matches(item);
}
return false;
}
}
@@ -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();
@@ -0,0 +1,410 @@
package codechicken.nei;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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.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 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 {
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.Manual", manualFavorites::contains);
API.addSubset("Favorites.Generated", stack -> FavoriteRecipes.autoFavorites.contains(stack));
}
private FavoriteRecipes() {}
public static void load() {
String worldPath = "global";
if (NEIClientConfig.getBooleanSetting("inventory.favorites.worldSpecific")) {
worldPath = NEIClientConfig.getWorldPath();
}
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
if (!dir.exists()) {
dir.mkdirs();
}
favoriteFile = new File(dir, "favorites.ini");
if (!favoriteFile.exists()) {
final File globalFavorites = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/favorites.ini");
final File configFavorites = new File(NEIClientConfig.configDir, "favorites.ini");
final File defaultFavorites = configFavorites.exists() ? configFavorites : globalFavorites;
if (defaultFavorites.exists()) {
try {
if (favoriteFile.createNewFile()) {
InputStream src = new FileInputStream(defaultFavorites);
OutputStream dst = new FileOutputStream(favoriteFile);
IOUtils.copy(src, dst);
src.close();
dst.close();
}
} catch (IOException e) {}
}
}
loadData();
}
private static void loadData() {
if (favoriteFile == null || !favoriteFile.exists()) {
return;
}
List<String> itemStrings;
try (FileInputStream reader = new FileInputStream(favoriteFile)) {
NEIClientConfig.logger.info("Loading favorites from file {}", favoriteFile);
itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load favorites from file {}", favoriteFile, e);
return;
}
final JsonParser parser = new JsonParser();
boolean hasErrors = false;
manualFavorites.clear();
for (String itemStr : itemStrings) {
if (itemStr.isEmpty() || itemStr.startsWith("#")) {
continue;
}
try {
JsonObject jsonObject = parser.parse(itemStr).getAsJsonObject();
if (jsonObject.get("item") != null && jsonObject.get("recipeId") != null) {
final NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item"));
final RecipeId recipeId = RecipeId.of(jsonObject.getAsJsonObject("recipeId"));
final ItemStack stack = StackInfo.loadFromNBT(itemStackNBT);
if (recipeId == null || stack == null) {
continue;
}
if (FAVORITE_ITEM.equals(recipeId.getHandlerName()) || RecipeHandlerRef.of(recipeId) != null) {
manualFavorites.add(stack, recipeId);
} else {
hasErrors = true;
}
}
} catch (Exception e) {
NEIClientConfig.logger.error("Failed to load favorite from json string: {}", itemStr);
hasErrors = true;
}
}
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) {
final RecipeId recipeId = manualFavorites.getRecipeId(stack);
return recipeId != null ? recipeId : autoFavorites.getRecipeId(stack);
}
return null;
}
public static ItemStack getFavorite(RecipeId recipeId) {
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 toggleFavoriteItem(ItemStack stack) {
if (stack == null) return;
if (containsManual(stack)) {
setFavorite(stack, null);
} else {
setFavorite(
stack,
RecipeId.of(
stack,
FavoriteRecipes.FAVORITE_ITEM,
Arrays.asList(new ItemStack(Blocks.fire, 1), stack)));
}
}
public static void setFavorite(ItemStack stack, RecipeId recipeId) {
if (stack == null) return;
manualFavorites.removeItemStack(stack);
if (recipeId != null) {
manualFavorites.removeRecipeId(recipeId);
manualFavorites.add(stack, recipeId);
}
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() {
if (favoriteFile == null) {
return;
}
final List<String> strings = new ArrayList<>();
for (Map.Entry<NBTTagCompound, RecipeId> entry : manualFavorites.getAllFavorites()) {
try {
final JsonObject line = new JsonObject();
line.add("item", NBTJson.toJsonObject(entry.getKey()));
line.add("recipeId", entry.getValue().toJsonObject());
strings.add(NBTJson.toJson(line));
} catch (JsonSyntaxException e) {
NEIClientConfig.logger.error("Failed to stringify favorites ItemStack to json string");
}
}
try (FileOutputStream output = new FileOutputStream(favoriteFile)) {
IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save favorites list to file {}", favoriteFile, e);
}
}
}
@@ -0,0 +1,328 @@
package codechicken.nei;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ChatAllowedCharacters;
import net.minecraft.util.EnumChatFormatting;
import org.lwjgl.opengl.GL11;
public class FormattedTextField extends GuiTextField {
public static interface TextFormatter {
TextFormatter DEFAULT = (text) -> text;
public String format(String text);
}
protected FontRenderer fontRenderer;
protected String rawText = "";
protected String formattedText = "";
protected String placeholder = "";
protected boolean editable = true;
protected int editableColor = 14737632;
protected int notEditableColor = 7368816;
protected TextFormatter formatter = TextFormatter.DEFAULT;
protected int lineScrollOffset = 0;
protected int frame = 0;
public FormattedTextField(FontRenderer fontRenderer, int xPosition, int yPosition, int width, int height) {
super(fontRenderer, xPosition, yPosition, width, height);
this.fontRenderer = fontRenderer;
}
public void setFormatter(TextFormatter formatter) {
this.formatter = formatter;
}
public TextFormatter getFormatter() {
return this.formatter;
}
public void setPlaceholder(String placeholder) {
this.placeholder = placeholder;
}
@Override
public void updateCursorCounter() {
super.updateCursorCounter();
++this.frame;
}
@Override
public void setFocused(boolean focus) {
if (focus && !this.isFocused()) {
this.frame = 0;
}
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();
int j = text.length();
super.setSelectionPos(p_146199_1_);
p_146199_1_ = Math.max(0, Math.min(p_146199_1_, j));
if (this.fontRenderer != null) {
if (this.lineScrollOffset > j) {
this.lineScrollOffset = j;
}
int k = this.getWidth();
String s = this.fontRenderer.trimStringToWidth(text.substring(this.lineScrollOffset), k);
int l = s.length() + this.lineScrollOffset;
if (p_146199_1_ == this.lineScrollOffset) {
this.lineScrollOffset -= this.fontRenderer.trimStringToWidth(text, k, true).length();
}
if (p_146199_1_ > l) {
this.lineScrollOffset += p_146199_1_ - l;
} else if (p_146199_1_ <= this.lineScrollOffset) {
this.lineScrollOffset -= this.lineScrollOffset - p_146199_1_;
}
this.lineScrollOffset = Math.max(0, Math.min(this.lineScrollOffset, j));
}
}
@Override
public void setTextColor(int p_146193_1_) {
super.setTextColor(p_146193_1_);
this.editableColor = p_146193_1_;
}
@Override
public void setDisabledTextColour(int p_146204_1_) {
super.setDisabledTextColour(p_146204_1_);
this.notEditableColor = p_146204_1_;
}
@Override
public void setEnabled(boolean p_146184_1_) {
super.setEnabled(p_146184_1_);
this.editable = p_146184_1_;
}
protected boolean beforeWrite(String text) {
return true;
}
@Override
public void writeText(String textPart) {
String newText = "";
String substr = ChatAllowedCharacters.filerAllowedCharacters(textPart);
int cursorPosition = getCursorPosition();
int maxStringLength = getMaxStringLength();
String text = getText();
if (text.isEmpty()) {
newText = substr.substring(0, Math.min(maxStringLength, substr.length()));
cursorPosition = newText.length();
} else {
int selectionEnd = getSelectionEnd();
String textStart = text.substring(0, Math.min(cursorPosition, selectionEnd));
String textEnd = text.substring(Math.max(cursorPosition, selectionEnd));
newText = textStart + substr + textEnd;
if (newText.length() > maxStringLength) {
newText = newText.substring(0, maxStringLength);
}
cursorPosition = Math.min(textStart.length() + substr.length(), newText.length());
}
if (beforeWrite(newText)) {
setText(newText);
setCursorPosition(cursorPosition);
}
}
@Override
public int getNthWordFromPos(int p_146183_1_, int p_146183_2_) {
return this.func_146197_a(p_146183_1_, p_146183_2_, true);
}
@Override
public void drawTextBox() {
if (!this.getVisible()) {
return;
}
if (this.getEnableBackgroundDrawing()) {
drawRect(
this.xPosition - 1,
this.yPosition - 1,
this.xPosition + this.width + 1,
this.yPosition + this.height + 1,
-6250336);
drawRect(
this.xPosition,
this.yPosition,
this.xPosition + this.width,
this.yPosition + this.height,
-16777216);
}
if (!this.rawText.equals(getText())) {
this.rawText = getText();
this.formattedText = this.formatter.format(this.rawText);
if (!EnumChatFormatting.getTextWithoutFormattingCodes(this.formattedText).toLowerCase()
.equals(this.rawText.toLowerCase())) {
this.formattedText = this.rawText;
}
}
if (!isFocused() && this.formattedText.isEmpty()) {
int x = getEnableBackgroundDrawing() ? this.xPosition + 4 : this.xPosition;
int y = getEnableBackgroundDrawing() ? this.yPosition + (this.height - 8) / 2 : this.yPosition;
this.fontRenderer.drawStringWithShadow(
this.fontRenderer.trimStringToWidth(this.placeholder, this.getWidth()),
x,
y,
this.notEditableColor);
return;
}
int firstCharacterIndex = getFormattedTextShift(this.lineScrollOffset);
String rawTextClipped = this.fontRenderer
.trimStringToWidth(this.rawText.substring(this.lineScrollOffset), this.getWidth());
String textClipped = this.formattedText
.substring(firstCharacterIndex, getFormattedTextShift(this.lineScrollOffset + rawTextClipped.length()));
int cursorPosition = getFormattedTextShift(getCursorPosition());
int selectionEnd = getFormattedTextShift(getSelectionEnd());
int color = this.editable ? this.editableColor : this.notEditableColor;
int cursorA = cursorPosition - firstCharacterIndex;
int cursorB = selectionEnd - firstCharacterIndex;
boolean flag = cursorA >= 0 && cursorA <= textClipped.length();
boolean flag1 = isFocused() && this.frame / 6 % 2 == 0 && flag;
boolean flag2 = getCursorPosition() < this.rawText.length() || this.rawText.length() >= getMaxStringLength();
int x = getEnableBackgroundDrawing() ? this.xPosition + 4 : this.xPosition;
int y = getEnableBackgroundDrawing() ? this.yPosition + (this.height - 8) / 2 : this.yPosition;
int x2 = x;
if (cursorB > textClipped.length()) {
cursorB = textClipped.length();
}
if (textClipped.length() > 0) {
String s1 = flag ? textClipped.substring(0, cursorA) : textClipped;
String colorA = getPreviousColor(firstCharacterIndex);
x2 = this.fontRenderer.drawStringWithShadow(colorA + s1, x, y, color);
}
int k1 = x2;
if (!flag) {
k1 = cursorA > 0 ? x + this.width : x;
} else if (flag2) {
k1 = x2 - 1;
--x2;
}
if (textClipped.length() > 0 && flag && cursorA < textClipped.length()) {
String colorB = getPreviousColor(firstCharacterIndex + cursorA);
this.fontRenderer.drawStringWithShadow(colorB + textClipped.substring(cursorA), x2, y, color);
}
if (flag1) {
if (flag2) {
Gui.drawRect(k1, y - 1, k1 + 1, y + 1 + this.fontRenderer.FONT_HEIGHT, -3092272);
} else {
this.fontRenderer.drawStringWithShadow("_", k1, y, color);
}
}
if (cursorB != cursorA) {
int l1 = x + this.fontRenderer.getStringWidth(textClipped.substring(0, cursorB));
drawCursorVertical(k1, y - 1, l1 - 1, y + 1 + this.fontRenderer.FONT_HEIGHT);
}
}
private void drawCursorVertical(int x1, int y1, int x2, int y2) {
int i1;
if (x1 < x2) {
i1 = x1;
x1 = x2;
x2 = i1;
}
if (y1 < y2) {
i1 = y1;
y1 = y2;
y2 = i1;
}
if (x2 > this.xPosition + this.width) {
x2 = this.xPosition + this.width;
}
if (x1 > this.xPosition + this.width) {
x1 = this.xPosition + this.width;
}
Tessellator tessellator = Tessellator.instance;
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);
tessellator.startDrawingQuads();
tessellator.addVertex((double) x1, (double) y2, 0.0D);
tessellator.addVertex((double) x2, (double) y2, 0.0D);
tessellator.addVertex((double) x2, (double) y1, 0.0D);
tessellator.addVertex((double) x1, (double) y1, 0.0D);
tessellator.draw();
GL11.glDisable(GL11.GL_COLOR_LOGIC_OP);
GL11.glEnable(GL11.GL_TEXTURE_2D);
}
private int getFormattedTextShift(int position) {
int shift = 0;
for (int i = 0; i < position; i++) {
while (this.formattedText.length() > i + shift + 1 && this.formattedText.charAt(i + shift) == '\u00a7'
&& isFormattingCode(this.formattedText.charAt(i + shift + 1))) {
shift += 2;
}
}
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);
}
position--;
}
return "";
}
}
@@ -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) {
@@ -12,10 +12,11 @@ import org.lwjgl.opengl.GL11;
import codechicken.lib.render.CCRenderState;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.AutoFocusWidget.INEIAutoFocusSearchEnable;
import codechicken.nei.api.INEIGuiHandler;
import codechicken.nei.api.TaggedInventoryArea;
public class GuiExtendedCreativeInv extends GuiContainer implements INEIGuiHandler {
public class GuiExtendedCreativeInv extends GuiContainer implements INEIGuiHandler, INEIAutoFocusSearchEnable {
public GuiExtendedCreativeInv(Container par1Container) {
super(par1Container);
@@ -59,13 +60,13 @@ public class GuiExtendedCreativeInv extends GuiContainer implements INEIGuiHandl
}
@Override
protected void handleMouseClick(Slot p_146984_1_, int p_146984_2_, int p_146984_3_, int p_146984_4_) {
protected void handleMouseClick(Slot slotIn, int slotId, int clickedButton, int clickType) {
// Hack for armor slots, because they are outside the container
if (p_146984_1_ != null && p_146984_4_ == 4 && p_146984_1_.xDisplayPosition < 0) {
p_146984_4_ = 0;
if (slotIn != null && clickType == 4 && slotIn.xDisplayPosition < 0) {
clickType = 0;
}
super.handleMouseClick(p_146984_1_, p_146984_2_, p_146984_3_, p_146984_4_);
super.handleMouseClick(slotIn, slotId, clickedButton, clickType);
}
}
+38 -42
View File
@@ -7,58 +7,54 @@ 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);
}
public void drawButton(Minecraft minecraft, int i, int j) {
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;
FontRenderer fontrenderer = minecraft.fontRenderer;
minecraft.renderEngine.bindTexture(guiTex);
GL11.glColor4f(1, 1, 1, 1);
boolean flag = i >= xPosition && j >= yPosition && i < xPosition + width && j < yPosition + height;
int k = getHoverState(flag);
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, i, j);
boolean mouseOver = x >= xPosition && y >= yPosition && x < xPosition + width && y < yPosition + height;
if (!enabled) drawCenteredString(
fontrenderer,
displayString,
xPosition + width / 2,
yPosition + (height - 8) / 2,
0xffa0a0a0);
else if (flag) drawCenteredString(
fontrenderer,
displayString,
xPosition + width / 2,
yPosition + (height - 8) / 2,
0xffffa0);
else drawCenteredString(
fontrenderer,
displayString,
xPosition + width / 2,
yPosition + (height - 8) / 2,
0xe0e0e0);
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) {
if (!enabled) {
return ColorUtils.buttonLabelInactive.getColor();
} else if (mouseOver) {
return ColorUtils.buttonLabelHover.getColor();
}
return ColorUtils.buttonLabelNormal.getColor();
}
protected void drawContent(Minecraft minecraft, int x, int y, boolean mouseOver) {
FontRenderer fontrenderer = minecraft.fontRenderer;
int color = getTextColour(mouseOver);
drawCenteredString(fontrenderer, displayString, xPosition + width / 2, yPosition + (height - 8) / 2, color);
}
}
@@ -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());
}
}
@@ -0,0 +1,114 @@
package codechicken.nei;
import static codechicken.nei.NEIClientUtils.translate;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.recipe.AutoCraftingManager;
public class HeldItemHandler implements IContainerInputHandler, IContainerTooltipHandler {
protected HeldItemHandler() {
}
public static void load() {
HeldItemHandler instance = new HeldItemHandler();
GuiContainerManager.addInputHandler(instance);
GuiContainerManager.addTooltipHandler(instance);
}
protected boolean contains(int mousex, int mousey) {
return NEIClientUtils.getHeldItem() != null && !ItemPanels.bookmarkPanel.inEditingState()
&& !AutoCraftingManager.processing()
&& (ItemPanels.bookmarkPanel.contains(mousex, mousey)
|| ItemPanels.itemPanel.containsWithSubpanels(mousex, mousey));
}
@Override
public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
if (contains(mousex, mousey)) {
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")
|| NEIClientUtils.mc().playerController.isInCreativeMode()) {
if (button == 1) {
NEIClientUtils.decreaseSlotStack(-999);
} else {
NEIClientUtils.deleteHeldItem();
}
} else {
NEIClientUtils.dropHeldItem();
}
return true;
}
return false;
}
@Override
public boolean keyTyped(GuiContainer gui, char keyChar, int keyCode) {
return false;
}
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
@Override
public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
return false;
}
@Override
public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) {
}
@Override
public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
}
@Override
public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) {
}
@Override
public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
}
@Override
public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) {
}
@Override
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
if (contains(mousex, mousey)) {
currenttip.clear();
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")
|| NEIClientUtils.mc().playerController.isInCreativeMode()) {
currenttip.add(EnumChatFormatting.RED + translate("itempanel.deleteItem"));
}
}
return currenttip;
}
}
@@ -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
@@ -0,0 +1,503 @@
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.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;
import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.NEIClientUtils.Alignment;
import codechicken.nei.recipe.AutoCraftingManager;
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.RecipeId;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.ReadableNumberConverter;
public class ItemCraftablesPanel
extends AbstractSubpanel<ItemsGrid<ItemCraftablesPanel.CraftablesGridSlot, MouseContext>> {
public static class CraftablesGridSlot extends ItemsGridSlot {
protected final long realAmount;
protected final boolean isFluidDisplay;
protected final RecipeId recipeId;
public CraftablesGridSlot(int slotIndex, int itemIndex, ItemStack itemStack, RecipeId recipeId) {
super(slotIndex, itemIndex, itemStack);
this.realAmount = StackInfo.getAmount(itemStack);
this.isFluidDisplay = StackInfo.isFluidDisplayItem(itemStack);
this.recipeId = recipeId;
}
@Override
public RecipeId getRecipeId() {
return this.recipeId;
}
@Override
public <M extends MouseContext> void afterDraw(Rectangle4i rect, M mouseContext) {
drawStackSize(rect);
}
protected void drawStackSize(Rectangle4i rect) {
long stackSize = this.realAmount;
if (stackSize > 1) {
final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2);
String amountString = "";
if (stackSize < 10_000) {
amountString = String.valueOf(stackSize);
} else {
amountString = ReadableNumberConverter.INSTANCE.toWideReadableForm(stackSize);
}
if (this.isFluidDisplay) {
amountString += "L";
}
NEIClientUtils.drawNEIOverlayText(
amountString,
new Rectangle4i(rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2),
panelFactor,
0xFFFFFF,
true,
this.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
}
}
}
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;
private Map<String, ArrayList<ICraftingHandler>> identHandlers = new HashMap<>();
private Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
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<>() {
protected List<CraftablesGridSlot> gridMask;
@Override
protected void onGridChanged() {
this.gridMask = null;
super.onGridChanged();
}
@Override
public List<CraftablesGridSlot> getMask() {
if (this.gridMask == null) {
final int maxSlotIndex = this.rows * this.columns;
final List<CraftablesGridSlot> gridMask = new ArrayList<>();
this.realItems.clear();
if (!ItemCraftablesPanel.this.availableRecipes.isEmpty()) {
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;
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;
}
@Override
protected MouseContext getMouseContext(int mousex, int mousey) {
final ItemsGridSlot hovered = getSlotMouseOver(mousex, mousey);
if (hovered != null) {
return new MouseContext(
hovered.slotIndex,
hovered.slotIndex / this.columns,
hovered.slotIndex % this.columns);
}
return null;
}
};
}
@Override
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
return ItemQuantityField.prepareStackWithQuantity(itemStack, StackInfo.getAmount(itemStack));
}
@Override
public void draw(int mousex, int mousey) {
if (!this.availableRecipes.isEmpty()) {
super.draw(mousex, mousey);
}
}
@Override
public int setPanelWidth(int width) {
if (getGuiContainer() != this.lastGuiContainer) updateCraftables();
final int columns = width / ItemsGrid.SLOT_SIZE;
final int useRows = NEIClientConfig.getIntSetting("inventory.craftables.useRows");
final int rows = (int) Math.min(Math.ceil(this.availableRecipes.size() * 1f / columns), useRows);
this.w = width;
this.h = 8 + ItemsGrid.SLOT_SIZE * Math.max(rows, 1);
return rows;
}
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()) {
clearCraftables();
} else if (this.lastPlayerInventory == null
|| Math.abs(System.currentTimeMillis() - this.lastPlayerInventorySync) >= MIN_DELAY) {
final GuiContainer firstGui = getGuiContainer();
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 != firstGui
|| showOnlyFavorites && this.lastFavoritesCount != FavoriteRecipes.size()
|| !inv.equals(this.lastPlayerInventory)) {
this.lastPlayerInventory = inv;
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 {
clearCraftables();
}
}
}
private void clearCraftables() {
if (!this.availableRecipes.isEmpty()) {
this.availableRecipes = Collections.emptyMap();
this.lastPlayerInventory = null;
this.grid.onGridChanged();
}
}
private GuiContainer getGuiContainer() {
final GuiContainer firstGui = NEIClientUtils.getGuiContainer();
return (firstGui instanceof GuiRecipe<?>gui) ? gui.firstGui : 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 Map<Item, List<ItemStack>> invByItem = indexInventoryByItem(snapshot.inventory.values());
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;
}
private List<IRecipeHandler> getAvailableHandlers(GuiContainer firstGui) {
final List<IRecipeHandler> availableHandlers = new ArrayList<>();
for (String ident : RecipeInfo.getOverlayHandlerIdents(firstGui)) {
availableHandlers.addAll(identHandlers.computeIfAbsent(ident, GuiCraftingRecipe::getCraftingHandlers));
}
return availableHandlers;
}
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 -> {
final List<PositionedStack> ingredientStacks = handler.getIngredientStacks(recipeIndex);
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);
}
}
}
}
});
return availableRecipes;
}
private boolean existsIngredients(List<PositionedStack> ingredients, Map<Item, List<ItemStack>> invByItem) {
final Map<ItemStack, Integer> usedItems = new HashMap<>();
for (PositionedStack pStack : ingredients) {
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);
} else {
return false;
}
}
return !usedItems.isEmpty();
}
}
@@ -1,206 +1,238 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.drawRect;
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.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import org.lwjgl.opengl.GL11;
import org.apache.commons.io.IOUtils;
import codechicken.nei.ItemPanel.ItemPanelSlot;
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
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 Widget {
public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemHistoryPanel.HistoryGridSlot, MouseContext>> {
protected int mouseDownSlot = -1;
protected ItemsGrid grid;
protected File historyFile = null;
public ItemHistoryPanel() {
grid = new ItemsGrid();
}
public static class HistoryGridSlot extends ItemsGridSlot {
public void draw(int mousex, int mousey) {
if (NEIClientConfig.getIntSetting("inventory.history.splittingMode") == 0) {
drawRect(x, y, w, h, NEIClientConfig.getSetting("inventory.history.historyColor").getHexValue());
} else {
drawSplittingArea(x, y, w, h, NEIClientConfig.getSetting("inventory.history.historyColor").getHexValue());
public HistoryGridSlot(int slotIndex, int itemIndex, ItemStack item) {
super(slotIndex, itemIndex, item);
}
grid.draw(mousex, mousey);
@Override
public RecipeId getRecipeId() {
return FavoriteRecipes.getFavorite(this.item);
}
}
public ItemHistoryPanel() {
this.grid = new ItemsGrid<>() {
protected List<HistoryGridSlot> gridMask;
@Override
protected void onGridChanged() {
this.gridMask = null;
super.onGridChanged();
}
@Override
public List<HistoryGridSlot> getMask() {
if (this.gridMask == null) {
this.gridMask = new ArrayList<>();
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;
}
@Override
protected MouseContext getMouseContext(int mousex, int mousey) {
final HistoryGridSlot hovered = getSlotMouseOver(mousex, mousey);
if (hovered != null) {
return new MouseContext(
hovered.slotIndex,
hovered.slotIndex / this.columns,
hovered.slotIndex % this.columns);
}
return null;
}
};
}
@Override
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
return ItemQuantityField.prepareStackWithQuantity(itemStack, 0);
}
@Override
public void draw(int mousex, int mousey) {
if (this.grid.size() > 0) {
super.draw(mousex, mousey);
}
}
@Override
public int setPanelWidth(int width) {
final int columns = width / ItemsGrid.SLOT_SIZE;
final int useRows = NEIClientConfig.getIntSetting("inventory.history.useRows");
final int rows = (int) Math.min(Math.ceil(this.grid.size() * 1f / columns), useRows);
this.w = width;
this.h = 8 + ItemsGrid.SLOT_SIZE * Math.max(rows, 1);
return rows;
}
public void update() {
grid.update();
this.splittingLineColor = NEIClientConfig.getSetting("inventory.history.color").getHexValue();
super.update();
}
public void addItem(ItemStack stack) {
if (stack != null) {
ItemStack is = stack.copy();
is.stackSize = 1;
ItemStack is = StackInfo.withAmount(stack, 0);
grid.realItems.removeIf(historyStack -> StackInfo.equalItemAndNBT(historyStack, stack, true));
grid.realItems.add(0, is);
this.grid.realItems.removeIf(historyStack -> StackInfo.equalItemAndNBT(historyStack, stack, true));
this.grid.realItems.add(0, is);
if (grid.realItems.size() > (grid.rows * grid.columns)) {
grid.realItems.remove(grid.rows * grid.columns);
if (this.grid.realItems.size() > Math.max(50, this.grid.rows * this.grid.columns)) {
this.grid.realItems.remove(this.grid.realItems.size() - 1);
}
grid.onItemsChanged();
this.grid.onItemsChanged();
}
}
public void resize(GuiContainer gui) {
grid.setGridSize(x, y, w, h);
grid.refresh(gui);
}
public void load() {
@Override
public ItemStack getStackMouseOver(int mousex, int mousey) {
ItemPanelSlot slot = getSlotMouseOver(mousex, mousey);
return slot == null ? null : slot.item;
}
public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
return grid.getSlotMouseOver(mousex, mousey);
}
private void drawSplittingArea(int x, int y, int width, int height, int color) {
float alpha = (float) (color >> 24 & 255) / 255.0F;
float red = (float) (color >> 16 & 255) / 255.0F;
float green = (float) (color >> 8 & 255) / 255.0F;
float blue = (float) (color & 255) / 255.0F;
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_LINE_STIPPLE);
GL11.glColor4f(red, green, blue, alpha);
GL11.glLineWidth(2F);
GL11.glLineStipple(2, (short) 0x00FF);
GL11.glBegin(GL11.GL_LINE_LOOP);
GL11.glVertex2i(x, y);
GL11.glVertex2i(x + width, y);
GL11.glVertex2i(x + width, y + height);
GL11.glVertex2i(x, y + height);
GL11.glEnd();
GL11.glLineStipple(1, (short) 0xFFFF);
GL11.glLineWidth(1F);
GL11.glDisable(GL11.GL_LINE_STIPPLE);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glColor4f(1F, 1F, 1F, 1F);
GL11.glPopMatrix();
}
@Override
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
if (mouseDownSlot >= 0 && ItemPanels.itemPanel.draggedStack == null
&& NEIClientUtils.getHeldItem() == null
&& NEIClientConfig.hasSMPCounterPart()) {
ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
if (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500) {
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(mouseDownSlot);
mouseDownSlot = -1;
}
if (!NEIClientConfig.getBooleanSetting("inventory.history.save")) {
return;
}
}
String worldPath = "global";
@Override
public boolean handleClick(int mousex, int mousey, int button) {
if (handleClickExt(mousex, mousey, button)) return true;
if (NEIClientUtils.getHeldItem() != null) {
if (!grid.contains(mousex, mousey)) {
return false;
}
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")) {
if (button == 1) {
NEIClientUtils.decreaseSlotStack(-999);
} else {
NEIClientUtils.deleteHeldItem();
}
} else {
NEIClientUtils.dropHeldItem();
}
return true;
if (NEIClientConfig.getBooleanSetting("inventory.history.worldSpecific")) {
worldPath = NEIClientConfig.getWorldPath();
}
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
if (hoverSlot != null) {
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
if (button == 2) {
if (hoverSlot.item != null) {
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(hoverSlot.slotIndex);
}
} else {
mouseDownSlot = hoverSlot.slotIndex;
}
return true;
if (!dir.exists()) {
dir.mkdirs();
}
return false;
}
final File historyFile = new File(dir, "history.ini");
@Override
public void mouseUp(int mousex, int mousey, int button) {
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
if (historyFile.equals(this.historyFile)) {
return;
}
if (hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && ItemPanels.itemPanel.draggedStack == null) {
ItemStack item = hoverSlot.item.copy();
List<String> itemStrings = Collections.emptyList();
if (NEIController.manager.window instanceof GuiRecipe || !NEIClientConfig.canCheatItem(item)) {
if (button == 0) {
GuiCraftingRecipe.openRecipeGui("item", item);
} else if (button == 1) {
GuiUsageRecipe.openRecipeGui("item", item);
}
mouseDownSlot = -1;
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;
}
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.slotIndex), button, -1);
}
mouseDownSlot = -1;
}
final JsonParser parser = new JsonParser();
final int maxSize = Math.max(50, this.grid.rows * this.grid.columns);
protected ItemStack getDraggedStackWithQuantity(int mouseDownSlot) {
ItemStack stack = grid.getItem(mouseDownSlot);
this.grid.realItems.clear();
if (stack != null) {
int amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
for (String itemStr : itemStrings) {
if (amount == 0) {
amount = stack.getMaxStackSize();
if (itemStr.isEmpty()) {
continue;
}
return NEIServerUtils.copyStack(stack, amount);
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);
}
}
return null;
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);
}
}
}
+76 -118
View File
@@ -2,26 +2,24 @@ package codechicken.nei;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
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.guihook.GuiContainerManager;
import codechicken.nei.util.ItemUntranslator;
public class ItemList {
@@ -37,11 +35,8 @@ public class ItemList {
* Updates to this should be synchronised on this
*/
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<>();
/**
* Unlike {@link LayoutManager#itemsLoaded}, this indicates whether item loading is actually finished or not.
*/
@@ -63,6 +58,20 @@ public class ItemList {
}
}
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;
@@ -73,7 +82,34 @@ public class ItemList {
@Override
public boolean matches(ItemStack item) {
return pattern.matcher(ItemInfo.getSearchName(item)).find();
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;
}
}
@@ -86,7 +122,7 @@ public class ItemList {
}
public AllMultiItemFilter(ItemFilter... filters) {
this(Arrays.asList(filters));
this(new LinkedList<>(Arrays.asList(filters)));
}
public AllMultiItemFilter() {
@@ -98,7 +134,8 @@ public class ItemList {
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);
NEIClientConfig.logger
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
}
return true;
@@ -120,9 +157,10 @@ public class ItemList {
@Override
public boolean matches(ItemStack item) {
for (ItemFilter filter : filters) try {
if (filter.matches(item)) return true;
if (filter != null && filter.matches(item)) return true;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + item + " with " + filter, e);
NEIClientConfig.logger
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
}
return false;
@@ -137,9 +175,10 @@ public class ItemList {
public static boolean itemMatchesAll(ItemStack item, List<ItemFilter> filters) {
for (ItemFilter filter : filters) {
try {
if (!filter.matches(item)) return false;
if (filter != null && !filter.matches(item)) return false;
} catch (Exception e) {
NEIClientConfig.logger.error("Exception filtering " + item + " with " + filter, e);
NEIClientConfig.logger
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
}
}
@@ -161,119 +200,38 @@ public class ItemList {
public static List<ItemFilter> getItemFilters() {
LinkedList<ItemFilter> filters = new LinkedList<>();
synchronized (itemFilterers) {
for (ItemFilterProvider p : itemFilterers) filters.add(p.getFilter());
for (ItemFilterProvider p : itemFilterers) {
filters.add(p.getFilter());
}
}
return filters;
}
public static final RestartableTask loadItems = new RestartableTask("NEI Item Loading") {
public static ForkJoinPool forkJoinPool;
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 = GuiContainerManager.concatenatedDisplayName(itemstack, false);
String s = name + "@" + (icon == null ? 0 : icon.hashCode());
if (!damageIconSet.contains(s)) {
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());
static {
final ForkJoinPool.ForkJoinWorkerThreadFactory factory = new ForkJoinPool.ForkJoinWorkerThreadFactory() {
private int workerId;
@Override
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
worker.setName("NEI-worker-thread-" + workerId++);
return worker;
}
}
@Override
@SuppressWarnings("unchecked")
public void execute() {
// System.out.println("Executing NEI Item Loading");
ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
loadFinished = false;
LinkedList<ItemStack> items = new LinkedList<>();
LinkedList<ItemStack> permutations = new LinkedList<>();
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
timer.setLimit(NEIClientConfig.getItemLoadingTimeout());
for (Item item : (Iterable<Item>) Item.itemRegistry) {
if (interrupted()) return;
if (item == null || erroredItems.contains(item)) continue;
try {
timer.reset(item);
permutations.clear();
permutations.addAll(ItemInfo.itemOverrides.get(item));
if (permutations.isEmpty()) item.getSubItems(item, null, permutations);
if (permutations.isEmpty()) damageSearch(item, permutations);
permutations.addAll(ItemInfo.itemVariants.get(item));
timer.reset();
items.addAll(permutations);
itemMap.putAll(item, permutations);
} catch (Throwable t) {
NEIServerConfig.logger.error("Removing item: " + item + " from list.", t);
erroredItems.add(item);
}
}
if (interrupted()) return;
ItemList.items = items;
ItemList.itemMap = itemMap;
for (ItemsLoadedCallback callback : loadCallbacks) callback.itemsLoaded();
updateFilter.restart();
loadFinished = true;
}
};
public static ForkJoinPool getPool(int poolSize) {
};
int poolSize = Runtime.getRuntime().availableProcessors() * 2 / 3;
if (poolSize < 1) poolSize = 1;
return new ForkJoinPool(poolSize);
forkJoinPool = new ForkJoinPool(poolSize, factory, null, false);
}
public static final int numProcessors = Runtime.getRuntime().availableProcessors();
public static ForkJoinPool forkJoinPool = getPool(numProcessors * 2 / 3);
// The item list pipeline lives in ItemListLoader; these references are kept for backward compatibility.
public static final RestartableTask loadItems = ItemListLoader.loadItems;
public static final RestartableTask updateFilter = new RestartableTask("NEI Item Filtering") {
public static final RestartableTask refreshItems = ItemListLoader.refreshItems;
@Override
public void execute() {
// System.out.println("Executing NEI Item Filtering");
ArrayList<ItemStack> filtered;
ItemFilter filter = getItemListFilter();
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;
ItemSorter.sort(filtered);
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);
}
};
}
+267 -76
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,124 +17,313 @@ 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);
hasSubtypes = true;
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;
}
@Override
public void addInformation(ItemStack itemstack, EntityPlayer par2EntityPlayer, List list, boolean par4) {
setDefaultTag(itemstack);
int meta = itemstack.getItemDamage();
if (meta == 0) {
meta = idPig;
}
Entity e = getEntity(meta);
list.add("\u00A7" + (e instanceof IMob ? "4" : "3") + IDtoNameMap.get(meta));
private String getMobTypeFromItemStack(ItemStack stack) {
int meta = getValidMetaData(stack);
return IDtoNameMap.get(meta);
}
public static EntityLiving getEntity(int ID) {
EntityLiving e = entityHashMap.get(ID);
if (e == null) {
World world = NEIClientUtils.mc().theWorld;
loadSpawners(world);
Class<?> clazz = (Class<?>) EntityList.IDtoClassMapping.get(ID);
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);
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 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);
}
}
public static EntityLiving getEntity(int id) {
EntityLiving cached = entityHashMap.get(id);
if (cached != null) {
return cached;
}
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);
}
entityHashMap.put(ID, e);
}
return e;
return pig;
}
public static void clearEntityReferences(World newWorld) {
entityHashMap.values().removeIf(e -> e.worldObj != newWorld);
public static void clearEntityReferences() {
entityHashMap.clear();
NEIClientConfig.logger.debug("Cleared all entity references from cache");
}
private void setDefaultTag(ItemStack itemstack) {
if (!IDtoNameMap.containsKey(itemstack.getItemDamage())) itemstack.setItemDamage(idPig);
}
public static void loadSpawners() {
if (loaded) {
return;
}
public static void loadSpawners(World world) {
if (loaded) return;
loaded = true;
HashMap<Class<Entity>, String> classToStringMapping = (HashMap<Class<Entity>, String>) EntityList.classToStringMapping;
HashMap<Class<Entity>, Integer> classToIDMapping = (HashMap<Class<Entity>, Integer>) EntityList.classToIDMapping;
for (Class<Entity> eclass : classToStringMapping.keySet()) {
if (!EntityLiving.class.isAssignableFrom(eclass)) continue;
synchronized (ItemMobSpawner.class) {
try {
EntityLiving entityliving = (EntityLiving) eclass.getConstructor(new Class[] { World.class })
.newInstance(world);
entityliving.isChild();
for (Map.Entry<Class<? extends Entity>, String> entry : EntityList.classToStringMapping.entrySet()) {
int id = classToIDMapping.get(eclass);
String name = classToStringMapping.get(eclass);
Class<? extends Entity> clazz = entry.getKey();
String name = entry.getValue();
if (name.equals("EnderDragon")) continue;
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);
IDtoNameMap.put(id, 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());
if (name.equals("Pig")) idPig = id;
} catch (Throwable ignored) {}
} 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;
}
IDtoNameMap.entrySet()
.removeIf(e -> getEntity(e.getKey()).getClass() == EntityPig.class && !e.getValue().equals("Pig"));
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 list) {
if (!NEIClientConfig.hasSMPCounterPart()) list.add(new ItemStack(item));
else for (int i : IDtoNameMap.keySet()) list.add(new ItemStack(item, 1, i));
public void getSubItems(Item item, CreativeTabs tab, List<ItemStack> list) {
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);
}
}
+164 -132
View File
@@ -1,17 +1,18 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.drawRect;
import java.util.ArrayList;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
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.ItemsPanelGrid.ItemsPanelGridSlot;
import codechicken.nei.util.NEIMouseUtils;
public class ItemPanel extends PanelWidget {
public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
/**
* Backwards compat :-/
@@ -31,67 +32,50 @@ public class ItemPanel extends PanelWidget {
return super.getStackMouseOver(mousex, mousey);
}
public Button more;
public Button less;
public ItemQuantityField quantity;
public ItemHistoryPanel historyPanel;
public ItemHistoryPanel historyPanel = new ItemHistoryPanel();
public ItemCraftablesPanel craftablesPanel = new ItemCraftablesPanel();
protected Button toggleGroups;
public static class ItemPanelSlot {
@Deprecated
public static class ItemPanelSlot extends ItemsPanelGridSlot {
public ItemStack item;
public int slotIndex;
public ItemPanelSlot(int idx, ItemStack stack) {
slotIndex = idx;
item = stack;
@Deprecated
public ItemPanelSlot(int idx, ItemStack item) {
this(idx, 0, item);
}
@Deprecated
public ItemPanelSlot(int idx) {
this(idx, ItemPanels.itemPanel.getGrid().getItem(idx));
}
}
protected static class ItemPanelGrid extends ItemsGrid {
public ArrayList<ItemStack> newItems;
public void setItems(ArrayList<ItemStack> items) {
newItems = items;
this(idx, 0, ItemPanels.itemPanel.getGrid().getItem(idx));
}
public void refresh(GuiContainer gui) {
private ItemPanelSlot(int slotIndex, int itemIndex, ItemStack item) {
super(slotIndex, itemIndex, item);
}
if (newItems != null) {
realItems = newItems;
newItems = null;
onItemsChanged();
protected static ItemPanelSlot of(ItemsPanelGridSlot slot) {
if (slot != null) {
ItemPanelSlot customSlot = new ItemPanelSlot(slot.slotIndex, slot.itemIndex, slot.item);
customSlot.groupIndex = slot.groupIndex;
customSlot.displayName = slot.displayName;
customSlot.bgItemStack = slot.bgItemStack;
customSlot.groupSize = slot.groupSize;
customSlot.extended = slot.extended;
customSlot.bgColor = slot.bgColor;
return customSlot;
}
super.refresh(gui);
}
@Override
protected void beforeDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {
if (PresetsWidget.inEditMode()) {
if (!PresetsWidget.isHidden(getItem(slotIdx))) drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555);
} else {
super.beforeDrawSlot(focused, slotIdx, rect);
}
}
@Override
public String getMessageOnEmpty() {
return ItemList.loadFinished ? null : NEIClientUtils.translate("itempanel.loading");
return null;
}
}
public ItemPanel() {
grid = new ItemPanelGrid();
this.grid = new ItemsPanelGrid();
}
public static void updateItemList(ArrayList<ItemStack> newItems) {
((ItemPanelGrid) ItemPanels.itemPanel.getGrid()).setItems(newItems);
ItemPanels.itemPanel.getGrid().setItems(newItems);
ItemPanels.itemPanel.realItems = newItems;
}
@@ -99,109 +83,108 @@ public class ItemPanel extends PanelWidget {
public void init() {
super.init();
more = new Button("+") {
toggleGroups = new Button("G") {
@Override
public boolean onButtonPress(boolean rightclick) {
if (rightclick) return false;
int modifier = NEIClientUtils.controlKey() ? 64 : NEIClientUtils.shiftKey() ? 10 : 1;
int quantity = NEIClientConfig.getItemQuantity() + modifier;
if (quantity < 0) {
quantity = 0;
}
ItemPanels.itemPanel.quantity.setText(Integer.toString(quantity));
return true;
public String getRenderLabel() {
return NEIClientUtils.translate("itempanel.collapsed.button");
}
@Override
public String getButtonTip() {
return NEIClientUtils.translate("itempanel.collapsed.button.tip");
}
};
less = new Button("-") {
@Override
public boolean onButtonPress(boolean rightclick) {
if (rightclick) return false;
int modifier = NEIClientUtils.controlKey() ? -64 : NEIClientUtils.shiftKey() ? -10 : -1;
int quantity = NEIClientConfig.getItemQuantity() + modifier;
if (quantity < 0) {
quantity = 0;
}
ItemPanels.itemPanel.quantity.setText(Integer.toString(quantity));
CollapsibleItems.toggleGroups(rightclick ? false : null);
ItemList.updateFilter.restart();
return true;
}
};
quantity = new ItemQuantityField("quantity");
historyPanel = new ItemHistoryPanel();
}
@Deprecated
public void scroll(int i) {
grid.shiftPage(i);
@Override
public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
// use ItemPanelSlot for Backwards compat
return ItemPanelSlot.of(this.grid.getSlotMouseOver(mousex, mousey));
}
public String getLabelText() {
return String.format("%d/%d", getPage(), Math.max(1, getNumPages()));
}
protected String getPositioningSettingName() {
return "world.panels.items";
@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;
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);
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);
return new Rectangle4i(gui.width - width - PADDING - paddingRight, paddingTop + PADDING, width, height);
}
public int getMarginLeft(GuiContainer gui) {
return (gui.width + gui.xSize) / 2 + PADDING;
}
@Override
protected int resizeHeader(GuiContainer gui) {
int marginTop = super.resizeHeader(gui);
public int getMarginTop(GuiContainer gui) {
return PADDING;
}
toggleGroups.w = pageNext.w;
toggleGroups.h = pageNext.h;
toggleGroups.y = pageNext.y;
toggleGroups.x = pageNext.x - toggleGroups.w - 2;
public int getWidth(GuiContainer gui) {
return gui.width - (gui.xSize + gui.width) / 2 - PADDING * 2;
}
public int getHeight(GuiContainer gui) {
return gui.height - getMarginTop(gui) - PADDING;
return marginTop;
}
protected int resizeFooter(GuiContainer gui) {
if (!NEIClientConfig.showItemQuantityWidget() && NEIClientConfig.isSearchWidgetCentered()) return 0;
int footerY = y + h;
int footerHeight = 0;
final int BUTTON_SIZE = 20;
more.w = less.w = BUTTON_SIZE;
quantity.h = BUTTON_SIZE;
if (grid.getPerPage() == 0) {
return 0;
}
if (NEIClientConfig.isSearchWidgetCentered()) {
more.h = less.h = BUTTON_SIZE;
more.x = x + w - BUTTON_SIZE;
more.y = less.y = quantity.y = y + h - BUTTON_SIZE;
less.x = x;
quantity.x = x + BUTTON_SIZE + 2;
quantity.w = more.x - quantity.x - 2;
} else {
quantity.x = (int) (x + (w * 0.7)) + 3;
quantity.y = y + h - BUTTON_SIZE;
quantity.w = (int) ((w * 0.3) - BUTTON_SIZE - 1);
more.h = less.h = BUTTON_SIZE / 2;
more.y = y + h - (more.h * 2);
less.x = more.x = quantity.x + quantity.w;
less.y = more.y + more.h;
if (NEIClientConfig.showItemQuantityWidget() || !NEIClientConfig.isSearchWidgetCentered()) {
footerY = LayoutManager.quantity.y;
footerHeight = LayoutManager.quantity.h + PanelWidget.PADDING;
}
if (NEIClientConfig.showHistoryPanelWidget()) {
historyPanel.x = x;
historyPanel.w = w;
historyPanel.h = ItemsGrid.SLOT_SIZE * NEIClientConfig.getIntSetting("inventory.history.useRows");
historyPanel.y = quantity.y - PanelWidget.PADDING - historyPanel.h;
return quantity.h + historyPanel.h + PanelWidget.PADDING * 2;
if (historyPanel.setPanelWidth(w) != 0) {
footerY = historyPanel.y = footerY - historyPanel.h;
footerHeight += historyPanel.h;
}
}
return quantity.h + PanelWidget.PADDING;
if (NEIClientConfig.showCraftablesPanelWidget()) {
craftablesPanel.x = x;
if (craftablesPanel.setPanelWidth(w) != 0) {
footerY = craftablesPanel.y = footerY - craftablesPanel.h;
footerHeight += craftablesPanel.h;
}
}
return footerHeight;
}
@Override
@@ -209,37 +192,86 @@ public class ItemPanel extends PanelWidget {
super.setVisible();
if (grid.getPerPage() > 0) {
if (NEIClientConfig.showItemQuantityWidget()) {
LayoutManager.addWidget(more);
LayoutManager.addWidget(less);
LayoutManager.addWidget(quantity);
if (!CollapsibleItems.isEmpty() && !grid.isEmpty()) {
LayoutManager.addWidget(toggleGroups);
}
if (NEIClientConfig.showCraftablesPanelWidget()) {
LayoutManager.addWidget(craftablesPanel);
}
if (NEIClientConfig.showHistoryPanelWidget()) {
LayoutManager.addWidget(historyPanel);
}
}
}
@Override
public void resize(GuiContainer gui) {
super.resize(gui);
historyPanel.resize(gui);
this.historyPanel.resize(gui);
this.craftablesPanel.resize(gui);
}
protected ItemStack getDraggedStackWithQuantity(int mouseDownSlot) {
ItemStack stack = grid.getItem(mouseDownSlot);
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
return ItemQuantityField.prepareStackWithQuantity(itemStack, 0);
}
if (stack != null) {
int amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
@Override
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> currenttip) {
if (amount == 0) {
amount = stack.getMaxStackSize();
if (!this.grid.forceExpand && !currenttip.isEmpty()) {
final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (hoverSlot != null && hoverSlot.groupIndex >= 0
&& NEIClientConfig.getBooleanSetting("inventory.collapsibleItems.customName")
&& !hoverSlot.extended
&& !"".equals(hoverSlot.displayName)) {
currenttip.clear();
currenttip.add(hoverSlot.displayName + GuiDraw.TOOLTIP_LINESPACE);
}
return NEIServerUtils.copyStack(stack, amount);
}
return null;
return super.handleItemTooltip(itemstack, mousex, mousey, currenttip);
}
@Override
public boolean handleClick(int mousex, int mousey, int button) {
if (button == 0 && !this.grid.forceExpand && NEIClientUtils.altKey()) {
final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (hoverSlot != null && hoverSlot.groupIndex >= 0) {
CollapsibleItems.setExpanded(hoverSlot.groupIndex, !hoverSlot.extended);
this.grid.setItems(this.grid.rawItems);
NEIClientUtils.playClickSound();
return true;
}
}
return super.handleClick(mousex, mousey, button);
}
@Override
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) {
final String message = hoverSlot.extended ? "itempanel.collapsed.hint.collapse"
: "itempanel.collapsed.hint.expand";
hotkeys.put(
NEIClientUtils.getKeyName(NEIClientUtils.ALT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
NEIClientUtils.translate(message, hoverSlot.groupSize));
}
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);
}
}
@@ -1,6 +1,11 @@
package codechicken.nei;
import static codechicken.nei.NEIClientUtils.translate;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import org.lwjgl.input.Keyboard;
import codechicken.nei.recipe.StackInfo;
public class ItemQuantityField extends TextField {
@@ -10,18 +15,80 @@ public class ItemQuantityField extends TextField {
field.setDisabledTextColour(0xFF303030);
}
public int intValue() {
return intValue(text());
@Override
protected void initInternalTextField() {
field = new FormattedTextField(Minecraft.getMinecraft().fontRenderer, 0, 0, 0, 0) {
{
setPlaceholder(NEIClientUtils.translate("itempanel.quantity.default"));
}
@Override
public String getText() {
String text = super.getText();
return !isFocused() && "0".equals(text) ? "" : text;
}
@Override
protected boolean beforeWrite(String text) {
if (text == null || text.isEmpty()) return true;
try {
return Integer.parseInt(text) >= 0;
} catch (NumberFormatException e) {
return false;
}
}
@Override
public boolean textboxKeyTyped(char keyChar, int keyID) {
if (!isFocused()) return false;
if (super.textboxKeyTyped(keyChar, keyID)) {
return true;
} else if (keyID == Keyboard.KEY_DOWN) {
setText(Integer.toString(Math.max(0, getInteger() - 1)));
return true;
} else if (keyID == Keyboard.KEY_UP) {
setText(Integer.toString(getInteger() + 1));
return true;
}
return false;
}
private int getInteger() {
try {
return Math.max(0, Integer.parseInt(text()));
} catch (NumberFormatException nfe) {
return 0;
}
}
};
field.setMaxStringLength(maxSearchLength);
field.setCursorPositionZero();
}
public int intValue(String s) {
private int getInteger() {
try {
return Math.max(0, Integer.parseInt(s));
return Math.max(0, Integer.parseInt(text()));
} catch (NumberFormatException nfe) {
return 0;
}
}
public static ItemStack prepareStackWithQuantity(ItemStack stack, long amount) {
if (stack == null) {
return null;
}
if (amount == 0) {
amount = NEIClientUtils.getScrollMultiplier(stack);
}
return StackInfo.withAmount(stack, amount);
}
@Override
public void gainFocus() {
if (text().equals("0")) {
@@ -36,35 +103,21 @@ public class ItemQuantityField extends TextField {
@Override
public void onTextChange(String oldText) {
NEIClientUtils.setItemQuantity(intValue());
}
@Override
public void draw(int mousex, int mousey) {
if (!focused() && intValue() == 0) {
field.setText(translate("itempanel.quantity.default"));
field.setEnabled(false);
super.draw(mousex, mousey);
field.setEnabled(true);
field.setText("0");
} else {
super.draw(mousex, mousey);
}
NEIClientUtils.setItemQuantity(getInteger());
}
@Override
public boolean onMouseWheel(int i, int mx, int my) {
if (!contains(mx, my)) return false;
int multiplier = 1;
if (NEIClientUtils.shiftKey()) {
multiplier = 10;
} else if (NEIClientUtils.controlKey()) {
multiplier = 64;
}
int quantity = intValue() + i * multiplier;
setText(Integer.toString(quantity));
setText(Integer.toString(Math.max(0, getInteger() + i * multiplier)));
return true;
}
+6 -17
View File
@@ -4,19 +4,19 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.StatCollector;
import codechicken.lib.config.ConfigTagParent;
import codechicken.nei.ItemList.ItemsLoadedCallback;
import codechicken.nei.api.API;
import codechicken.nei.api.ItemInfo;
import codechicken.nei.config.GuiItemSorter;
import codechicken.nei.config.OptionOpenGui;
public class ItemSorter implements Comparator<ItemStack>, ItemsLoadedCallback {
public class ItemSorter implements Comparator<ItemStack> {
public static class SortEntry {
@@ -44,12 +44,10 @@ public class ItemSorter implements Comparator<ItemStack>, ItemsLoadedCallback {
public static final ItemSorter instance = new ItemSorter();
// optimisations
public HashMap<ItemStack, Integer> ordering = null;
public Map<ItemStack, Integer> ordering = new HashMap<>();
public static void sort(ArrayList<ItemStack> items) {
public static void sort(List<ItemStack> items) {
try {
// items = (ArrayList<ItemStack>)
// items.parallelStream().sorted(instance).collect(Collectors.toList());
items.sort(instance);
} catch (Exception e) {
NEIClientConfig.logger.error("Exception sorting item list", e);
@@ -65,14 +63,6 @@ public class ItemSorter implements Comparator<ItemStack>, ItemsLoadedCallback {
return 0;
}
@Override
public void itemsLoaded() {
HashMap<ItemStack, Integer> newMap = new HashMap<>();
int i = 0;
for (ItemStack stack : ItemList.items) newMap.put(stack, i++);
ordering = newMap;
}
public static SortEntry find(String name) {
for (SortEntry e : entries) if (e.name.equals(name)) return e;
@@ -123,8 +113,8 @@ public class ItemSorter implements Comparator<ItemStack>, ItemsLoadedCallback {
return compareInt(id1, id2);
});
API.addSortOption("nei.itemsort.name", (o1, o2) -> {
String name1 = ItemInfo.getSearchName(o1);
String name2 = ItemInfo.getSearchName(o2);
String name1 = o1.getDisplayName();
String name2 = o2.getDisplayName();
return name1.compareTo(name2);
});
tag.getTag("inventory.itemsort").setDefaultValue(getSaveString(list));
@@ -136,7 +126,6 @@ public class ItemSorter implements Comparator<ItemStack>, ItemsLoadedCallback {
list = fromSaveString(activeTag().getValue());
}
});
ItemList.loadCallbacks.add(instance);
}
public static String getSaveString(List<SortEntry> list) {
@@ -0,0 +1,143 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import codechicken.nei.recipe.StackInfo;
public class ItemStackAmount {
private final Map<NBTTagCompound, Long> itemMap = new LinkedHashMap<>();
public void putAll(ItemStackAmount amounts) {
for (Map.Entry<NBTTagCompound, Long> entry : amounts.itemMap.entrySet()) {
this.itemMap.put(entry.getKey(), entry.getValue() + this.itemMap.getOrDefault(entry.getKey(), 0L));
}
}
public void addAll(Iterable<ItemStack> items) {
for (ItemStack stack : items) {
add(stack);
}
}
public void add(ItemStack item) {
add(item, (long) StackInfo.getAmount(item));
}
public void add(ItemStack stack, Long value) {
if (stack == null || stack.getItem() == null) return;
final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false);
if (key != null) {
this.itemMap.put(key, value + this.itemMap.getOrDefault(key, 0L));
}
}
public Long get(ItemStack stack) {
if (stack == null || stack.getItem() == null) return null;
final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false);
return this.itemMap.get(key);
}
public void put(ItemStack stack, long value) {
if (stack == null || stack.getItem() == null) return;
final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false);
if (key != null) {
this.itemMap.put(key, value);
}
}
public long getOrDefault(ItemStack stack, long defaultAmount) {
final Long e = get(stack);
return e == null ? defaultAmount : e;
}
public boolean contains(ItemStack stack) {
return get(stack) != null;
}
public void clear() {
this.itemMap.clear();
}
public Long remove(ItemStack stack) {
if (stack == null || stack.getItem() == null) return null;
final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false);
return this.itemMap.remove(key);
}
public boolean removeIf(Predicate<Map.Entry<NBTTagCompound, Long>> predicate) {
return this.itemMap.entrySet().removeIf(predicate);
}
public Set<Map.Entry<NBTTagCompound, Long>> entrySet() {
return this.itemMap.entrySet();
}
public List<ItemStack> values() {
List<ItemStack> list = new ArrayList<>();
for (Map.Entry<NBTTagCompound, Long> entry : this.itemMap.entrySet()) {
list.add(StackInfo.loadFromNBT(entry.getKey(), Math.max(0, entry.getValue())));
}
return list;
}
public int size() {
return this.itemMap.size();
}
public boolean isEmpty() {
return this.itemMap.isEmpty();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ItemStackAmount items) {
return this.itemMap.equals(items.itemMap);
}
return false;
}
public static ItemStackAmount of(ItemStackAmount map) {
ItemStackAmount result = new ItemStackAmount();
result.itemMap.putAll(map.itemMap);
return result;
}
public static ItemStackAmount of(ItemStackMap<Long> map) {
ItemStackAmount result = new ItemStackAmount();
for (ItemStackMap.Entry<Long> entry : map.entries()) {
result.put(entry.key, entry.value);
}
return result;
}
public static ItemStackAmount of(Iterable<ItemStack> items) {
ItemStackAmount result = new ItemStackAmount();
for (ItemStack stack : items) {
result.add(stack);
}
return result;
}
}
@@ -5,9 +5,11 @@ import static codechicken.lib.inventory.InventoryUtils.newItemStack;
import static net.minecraftforge.oredict.OreDictionary.WILDCARD_VALUE;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -24,8 +26,13 @@ public class ItemStackMap<T> {
public final int damage;
public final NBTTagCompound tag;
private final int hashCode;
public StackMetaKey(int damage, NBTTagCompound tag) {
if (tag != null && tag.hasNoTags()) {
tag = null;
}
this.hashCode = Objects.hashCode(damage, tag);
this.damage = damage;
this.tag = tag;
}
@@ -35,12 +42,11 @@ public class ItemStackMap<T> {
}
public int hashCode() {
return Objects.hashCode(damage, tag);
return this.hashCode;
}
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);
}
}
@@ -191,7 +197,7 @@ public class ItemStackMap<T> {
WILDCARD_TAG.setBoolean("*", true);
}
private final HashMap<Item, DetailMap> itemMap = new HashMap<>();
private final Map<Item, DetailMap> itemMap = new LinkedHashMap<>();
private int size;
public T get(ItemStack key) {
@@ -211,13 +217,26 @@ 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);
}
public T computeIfAbsent(ItemStack key, Function<ItemStack, ? extends T> mappingFunction) {
T value;
if ((value = get(key)) == null) {
T newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return value;
}
public void clear() {
itemMap.clear();
size = 0;
}
public T remove(ItemStack key) {
@@ -1,7 +1,5 @@
package codechicken.nei;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -29,11 +27,16 @@ public class ItemStackSet extends ItemStackMap<ItemStack> implements ItemFilter
put(item, item);
}
public ItemStackSet addAll(List<ItemStack> items) {
public ItemStackSet addAll(Iterable<ItemStack> items) {
for (ItemStack item : items) add(item);
return this;
}
public ItemStackSet removeAll(Iterable<ItemStack> items) {
for (ItemStack item : items) remove(item);
return this;
}
public boolean contains(ItemStack item) {
return get(item) != null;
}
+277
View File
@@ -0,0 +1,277 @@
package codechicken.nei;
import java.awt.Point;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.recipe.StackInfo;
public class ItemZoom extends Widget implements IContainerInputHandler {
public static final int MIN_ZOOM = 0;
public static final int MAX_ZOOM = 1000;
private static final int MIN_SCALE = 2;
private static final int SLOT_SIZE = 16;
private Boolean previousKeyboardRepeatEnabled = null;
private ItemStack stack = null;
private String displayName = "";
private int availableAreaWidth = 0;
private float xPosition = 0;
private float yPosition = 0;
private float scale = 1;
public ItemZoom() {
GuiContainerManager.addInputHandler(this);
}
@Override
public void draw(int mx, int my) {
if (this.stack != null && this.availableAreaWidth >= SLOT_SIZE * 2
&& (NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled")
|| KeyManager.isKeyDown("itemzoom.hold"))
&& GuiContainerManager.shouldShowTooltip(NEIClientUtils.getGuiContainer())) {
final float inverseScaleFactor = 1.0f / this.scale;
final float screenScale = getScreenScale();
final float size = SLOT_SIZE * this.scale;
int shiftText = 10;
GL11.glPushMatrix();
GL11.glScaled(screenScale, screenScale, 1);
GL11.glScaled(this.scale, this.scale, 2);
GL11.glTranslated(this.xPosition * inverseScaleFactor, this.yPosition * inverseScaleFactor, 0);
GuiContainerManager.drawItem(0, 0, this.stack, false, "");
GL11.glPopMatrix();
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
GuiContainerManager.enable2DRender();
if (NEIClientConfig.getBooleanSetting("inventory.itemzoom.showName")) {
String dispalyName = NEIClientUtils
.cropText(GuiDraw.fontRenderer, this.displayName, this.availableAreaWidth);
GuiDraw.drawStringC(
dispalyName,
(int) ((this.xPosition + size / 2) * screenScale),
(int) ((this.yPosition + size) * screenScale) + shiftText,
NEIClientConfig.getSetting("inventory.itemzoom.nameColor").getHexValue());
shiftText += GuiDraw.fontRenderer.FONT_HEIGHT;
}
if (NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled")
&& NEIClientConfig.getBooleanSetting("inventory.itemzoom.helpText")) {
String keyName = KeyManager.getKeyName("itemzoom.toggle", NEIClientUtils.SHIFT_HASH);
if (keyName != null) {
String helpText = NEIClientUtils.translate("itemzoom.toggle", keyName);
List<String> lines = GuiDraw.fontRenderer
.listFormattedStringToWidth(helpText, this.availableAreaWidth);
for (String line : lines) {
GuiDraw.drawStringC(
line,
(int) ((this.xPosition + size / 2) * screenScale),
(int) ((this.yPosition + size) * screenScale) + 10 + shiftText,
0x66555555);
shiftText += GuiDraw.fontRenderer.FONT_HEIGHT;
}
}
}
GL11.glPopAttrib();
}
}
public void resize(GuiContainer gui) {
final ItemStack stack = getStackMouseOver(gui);
if (stack != null && (!NEIClientConfig.getBooleanSetting("inventory.itemzoom.onlySolid")
|| Block.getBlockFromItem(stack.getItem()).getMaterial().isSolid())) {
final float screenScale = 1.0f / getScreenScale();
final float availableAreaWidth = (gui.width - gui.xSize) / 2 * screenScale;
final float availableAreaHeight = gui.height * screenScale;
final Point mouse = GuiDraw.getMousePosition();
this.scale = Math.round(getZoomAmount(gui) * getPointSize(gui) / SLOT_SIZE);
this.yPosition = (availableAreaHeight - this.scale * SLOT_SIZE) / 2;
this.xPosition = (availableAreaWidth - this.scale * SLOT_SIZE) / 2;
this.availableAreaWidth = (int) (availableAreaWidth / screenScale);
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 {
this.displayName = stack.getDisplayName();
} catch (Throwable ignored) {
this.displayName = "Unnamed";
}
this.stack = StackInfo.loadFromNBT(StackInfo.itemStackToNBT(stack), 0);
} else {
this.stack = null;
this.displayName = "";
}
}
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;
return availableAreaWidth / MAX_ZOOM;
}
private float getScreenScale() {
return Minecraft.getMinecraft().currentScreen.width * 1f / Minecraft.getMinecraft().displayWidth;
}
private int getZoomAmount(GuiContainer gui) {
return Math.max(
Math.round(SLOT_SIZE * MIN_SCALE / getPointSize(gui)),
NEIClientConfig.getIntSetting("inventory.itemzoom.zoom"));
}
private void increaseZoom(GuiContainer gui) {
final float pointSize = getPointSize(gui);
final float scale = Math.round(getZoomAmount(gui) * pointSize / SLOT_SIZE);
NEIClientConfig.setIntSetting(
"inventory.itemzoom.zoom",
Math.min(MAX_ZOOM, Math.round((scale + 1) / (pointSize / SLOT_SIZE))));
}
private void decreaseZoom(GuiContainer gui) {
final float pointSize = getPointSize(gui);
final float scale = Math.round(getZoomAmount(gui) * pointSize / SLOT_SIZE);
NEIClientConfig.setIntSetting(
"inventory.itemzoom.zoom",
Math.max(MIN_ZOOM, Math.round(Math.max(MIN_SCALE, scale - 1) / (pointSize / SLOT_SIZE))));
}
private ItemStack getStackMouseOver(GuiContainer gui) {
if (NEIClientConfig.getBooleanSetting("inventory.itemzoom.neiOnly")) {
final Point mouse = GuiDraw.getMousePosition();
ItemStack stack = ItemPanels.itemPanel.getStackMouseOver(mouse.x, mouse.y);
if (stack == null) {
stack = ItemPanels.bookmarkPanel.getStackMouseOver(mouse.x, mouse.y);
}
if (stack == null) {
stack = ItemPanels.itemPanel.historyPanel.getStackMouseOver(mouse.x, mouse.y);
}
return stack;
}
return GuiContainerManager.getStackMouseOver(gui);
}
@Override
public boolean keyTyped(GuiContainer gui, char keyChar, int keyCode) {
return false;
}
@Override
public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
return false;
}
@Override
public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) {}
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
@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 && KeyManager.isKeyDown("itemzoom.hold")) {
return true;
}
if (this.stack != null && KeyManager.isKeyDown("itemzoom.zoom_in")) {
increaseZoom(gui);
previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled();
Keyboard.enableRepeatEvents(true);
return true;
}
if (this.stack != null && KeyManager.isKeyDown("itemzoom.zoom_out")) {
decreaseZoom(gui);
previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled();
Keyboard.enableRepeatEvents(true);
return true;
}
if (previousKeyboardRepeatEnabled != null) {
Keyboard.enableRepeatEvents(previousKeyboardRepeatEnabled);
previousKeyboardRepeatEnabled = null;
}
return false;
}
@Override
public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {}
@Override
public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) {}
@Override
public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
return false;
}
@Override
public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {}
@Override
public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) {}
}
+351 -184
View File
@@ -1,12 +1,10 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.drawRect;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
import java.util.Objects;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.ScaledResolution;
@@ -17,16 +15,254 @@ import net.minecraft.client.shader.Framebuffer;
import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemPanel.ItemPanelSlot;
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;
public class ItemsGrid {
public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends ItemsGrid.MouseContext> {
public static final int SLOT_SIZE = 18;
protected static class ScreenCapture {
private long nextCacheRefresh = 0;
private final Framebuffer framebuffer;
public ScreenCapture() {
this.framebuffer = new Framebuffer(1, 1, true);
this.framebuffer.setFramebufferColor(0.0F, 0.0F, 0.0F, 0.0F);
Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(false);
}
protected int getGridRenderingCacheFPS(int mode) {
return mode == 1 ? NEIClientConfig.getIntSetting("inventory.gridRenderingCacheFPS") : 0;
}
public boolean needRefresh(int mode) {
final Minecraft minecraft = Minecraft.getMinecraft();
return this.nextCacheRefresh == 0 || this.framebuffer.framebufferWidth != minecraft.displayWidth
|| framebuffer.framebufferHeight != minecraft.displayHeight
|| getGridRenderingCacheFPS(mode) > 0 && this.nextCacheRefresh < System.currentTimeMillis();
}
public void refreshBuffer() {
this.nextCacheRefresh = 0;
}
public void captureScreen(Runnable callback, int mode) {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
resetFramebuffer();
this.framebuffer.bindFramebuffer(false);
/* Set up some rendering state needed for items to work correctly */
GL11.glDisable(GL11.GL_BLEND);
GL11.glDepthMask(true);
OpenGlHelper.glBlendFunc(
GL11.GL_SRC_ALPHA,
GL11.GL_ONE_MINUS_SRC_ALPHA,
GL11.GL_SRC_ALPHA,
GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
callback.run();
GL11.glPopAttrib();
Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(false);
this.nextCacheRefresh = System.currentTimeMillis() + (1000 / Math.max(1, getGridRenderingCacheFPS(mode)));
}
private void resetFramebuffer() {
final Minecraft minecraft = Minecraft.getMinecraft();
if (this.framebuffer.framebufferWidth != minecraft.displayWidth
|| framebuffer.framebufferHeight != minecraft.displayHeight) {
this.framebuffer.createBindFramebuffer(minecraft.displayWidth, minecraft.displayHeight);
this.framebuffer.setFramebufferFilter(GL11.GL_NEAREST);
} else {
this.framebuffer.framebufferClear();
}
// 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(
0,
0,
minecraft.displayWidth,
minecraft.displayHeight,
0,
0,
minecraft.displayWidth,
minecraft.displayHeight,
GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT,
GL11.GL_NEAREST);
}
public void renderCapturedScreen() {
this.framebuffer.bindFramebufferTexture();
final Tessellator tessellator = Tessellator.instance;
final Minecraft minecraft = Minecraft.getMinecraft();
final ScaledResolution scaledresolution = new ScaledResolution(
minecraft,
minecraft.displayWidth,
minecraft.displayHeight);
/* Set up some rendering state needed for items to work correctly */
GL11.glEnable(GL11.GL_BLEND);
OpenGlHelper
.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glEnable(GL11.GL_TEXTURE_2D);
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0, scaledresolution.getScaledHeight_double(), 0.0, 0, 0);
tessellator.addVertexWithUV(
scaledresolution.getScaledWidth_double(),
scaledresolution.getScaledHeight_double(),
0.0,
1,
0);
tessellator.addVertexWithUV(scaledresolution.getScaledWidth_double(), 0, 0.0, 1, 1);
tessellator.addVertexWithUV(0, 0, 0, 0, 1);
tessellator.draw();
this.framebuffer.unbindFramebufferTexture();
}
}
public static class ItemsGridSlot {
protected static final float DEFAULT_SLOT_SIZE = 18f;
protected static final float LINE_WIDTH = 0.75f;
public final ItemStack item;
public final int slotIndex;
public final int itemIndex;
public Color borderColor = Color.BLACK;
public boolean borderLeft = false;
public boolean borderTop = false;
public boolean borderRight = false;
public boolean borderBottom = false;
public ItemsGridSlot(int slotIndex, int itemIndex, ItemStack itemStack) {
this.slotIndex = slotIndex;
this.itemIndex = itemIndex;
this.item = itemStack;
}
public ItemStack getItemStack() {
return this.item;
}
public <M extends MouseContext> void beforeDraw(Rectangle4i rect, M mouseContext) {
if (mouseContext != null && mouseContext.slotIndex == this.slotIndex) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, HIGHLIGHT_COLOR);
}
}
public <M extends MouseContext> void afterDraw(Rectangle4i rect, M mouseContext) {}
public RecipeId getRecipeId() {
return null;
}
public void drawItem(Rectangle4i rect) {
drawItem(getItemStack(), rect);
}
protected void drawItem(ItemStack stack, Rectangle4i rect) {
if (rect.w != DEFAULT_SLOT_SIZE) {
final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2);
GL11.glTranslatef(rect.x + 1, rect.y + 1, 0);
GL11.glScaled(panelFactor, panelFactor, 1);
GuiContainerManager.drawItem(0, 0, stack, true, "");
GL11.glScaled(1f / panelFactor, 1f / panelFactor, 1);
GL11.glTranslatef(-1 * (rect.x + 1), -1 * (rect.y + 1), 0);
} else {
GuiContainerManager.drawItem(rect.x + 1, rect.y + 1, stack, true, "");
}
}
public void drawBorder(Rectangle4i rect) {
float halfLineWidth = LINE_WIDTH / 2f;
if (this.borderLeft) {
NEIClientUtils
.drawRect(rect.x - halfLineWidth, rect.y + halfLineWidth, LINE_WIDTH, rect.h, this.borderColor);
}
if (this.borderRight) {
NEIClientUtils.drawRect(
rect.x + rect.w - halfLineWidth,
rect.y - halfLineWidth,
LINE_WIDTH,
rect.h,
this.borderColor);
}
if (this.borderTop) {
NEIClientUtils
.drawRect(rect.x - halfLineWidth, rect.y - halfLineWidth, rect.w, LINE_WIDTH, this.borderColor);
}
if (this.borderBottom) {
NEIClientUtils.drawRect(
rect.x + halfLineWidth,
rect.y + rect.h - halfLineWidth,
rect.w,
LINE_WIDTH,
this.borderColor);
}
}
}
public static class MouseContext {
public final int slotIndex;
public final int columnIndex;
public final int rowIndex;
public MouseContext(int slotIndex, int rowIndex, int columnIndex) {
this.slotIndex = slotIndex;
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
}
@Override
public int hashCode() {
return Objects.hash(this.rowIndex, this.slotIndex, this.columnIndex);
}
public boolean equals(Object obj) {
if (obj instanceof MouseContext context) {
return this.columnIndex == context.columnIndex && this.rowIndex == context.rowIndex
&& this.slotIndex == context.slotIndex;
}
return false;
}
}
public static int SLOT_SIZE = 18;
public static final Color HIGHLIGHT_COLOR = new Color(0xee555555, true);
protected int width;
protected int height;
@@ -43,29 +279,27 @@ public class ItemsGrid {
protected int rows;
protected int columns;
protected List<Integer> gridMask = null;
protected boolean[] invalidSlotMap;
protected Label messageLabel = new Label(getMessageOnEmpty(), true);
@Nullable
private Framebuffer framebuffer = null;
protected boolean refreshBuffer = true;
protected ScreenCapture screenCapture = null;
public ArrayList<ItemStack> getItems() {
return realItems;
}
public ItemStack getItem(int idx) {
return realItems.get(idx);
public ItemStack getItem(int itemIndex) {
return realItems.get(itemIndex);
}
public int size() {
return realItems.size();
}
public boolean isEmpty() {
return realItems.isEmpty();
}
public int indexOf(ItemStack stackA, boolean useNBT) {
for (int idx = 0; idx < realItems.size(); idx++) {
@@ -107,23 +341,32 @@ public class ItemsGrid {
}
public int getLastRowIndex() {
return this.columns > 0 ? (int) Math.ceil((float) this.getMask().size() / this.columns) - 1 : 0;
final List<T> mask = getMask();
return this.columns > 0 && !mask.isEmpty()
? (int) Math.ceil((float) (mask.get(mask.size() - 1).slotIndex + 1) / this.columns) - 1
: 0;
}
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) {
@@ -152,16 +395,20 @@ public class ItemsGrid {
}
protected void onGridChanged() {
refreshBuffer = true;
this.gridMask = null;
if (this.screenCapture != null) {
this.screenCapture.refreshBuffer();
}
}
protected void onItemsChanged() {
public void onItemsChanged() {
onGridChanged();
}
public void refresh(GuiContainer gui) {
updateGuiOverlapSlots(gui);
final int gridRenderingCacheMode = getGridRenderingCacheMode();
if (gridRenderingCacheMode <= 0 || screenCapture == null || screenCapture.needRefresh(gridRenderingCacheMode)) {
updateGuiOverlapSlots(gui);
}
shiftPage(0);
}
@@ -173,11 +420,14 @@ public class ItemsGrid {
invalidSlotMap = new boolean[rows * columns];
perPage = columns * rows;
if (NEIClientConfig.optimizeGuiOverlapComputation()) {
checkGuiOverlap(gui, 0, columns - 2, 1);
checkGuiOverlap(gui, columns - 1, 1, -1);
} else {
checkGuiOverlap(gui, 0, columns, 1);
if (gui != null) {
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
if (NEIClientConfig.optimizeGuiOverlapComputation()) {
checkGuiOverlap(gui, handlers, 0, columns - 2, 1);
checkGuiOverlap(gui, handlers, columns - 1, 1, -1);
} else {
checkGuiOverlap(gui, handlers, 0, columns, 1);
}
}
if (oldRows != rows || oldColumns != columns || !Arrays.equals(oldSlotMap, invalidSlotMap)) {
@@ -185,7 +435,7 @@ public class ItemsGrid {
}
}
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) {
@@ -195,7 +445,7 @@ public class ItemsGrid {
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--;
@@ -204,20 +454,37 @@ public class ItemsGrid {
}
}
public Rectangle4i getItemRect(int idx) {
final List<Integer> mask = getMask();
for (int i = 0; i < mask.size(); i++) {
if (mask.get(i) != null && idx == mask.get(i)) {
return getSlotRect(i);
}
public T getSlotMouseOver(int mousex, int mousey) {
if (!contains(mousex, mousey)) {
return null;
}
return null;
final int overRow = getRowIndex(mousey);
final int overColumn = getColumnIndex(mousex);
final int slt = columns * overRow + overColumn;
if (overRow >= rows || overColumn >= columns) {
return null;
}
return getSlotBySlotIndex(slt);
}
public Rectangle4i getSlotRect(int i) {
return getSlotRect(i / columns, i % columns);
public T getSlotBySlotIndex(int slotIndex) {
return getMask().stream().filter(item -> item.slotIndex == slotIndex).findAny().orElse(null);
}
public T getSlotByItemIndex(int itemIndex) {
return getMask().stream().filter(item -> item.itemIndex == itemIndex).findAny().orElse(null);
}
public Rectangle4i getItemRect(int itemIndex) {
final T item = getSlotByItemIndex(itemIndex);
return item != null ? getSlotRect(item.slotIndex) : null;
}
public Rectangle4i getSlotRect(int slotIndex) {
return getSlotRect(slotIndex / columns, slotIndex % columns);
}
public Rectangle4i getSlotRect(int row, int column) {
@@ -228,99 +495,49 @@ public class ItemsGrid {
SLOT_SIZE);
}
public int getRowIndex(int mousey) {
return (mousey - this.marginTop) / SLOT_SIZE;
}
public int getColumnIndex(int mousex) {
return (mousex - this.marginLeft - this.paddingLeft) / SLOT_SIZE;
}
public boolean isInvalidSlot(int index) {
return invalidSlotMap[index];
}
protected List<Integer> getMask() {
public abstract List<T> getMask();
if (this.gridMask == null) {
this.gridMask = new ArrayList<>();
int idx = page * perPage;
for (int i = 0; i < rows * columns && idx < size(); i++) {
this.gridMask.add(isInvalidSlot(i) ? null : idx++);
}
protected abstract M getMouseContext(int mousex, int mousey);
protected void beforeDrawItems(int mousex, int mousey, M mouseContext) {
for (T item : getMask()) {
item.beforeDraw(getSlotRect(item.slotIndex), mouseContext);
}
return this.gridMask;
}
private void beforeDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) {
final List<Integer> mask = getMask();
for (int i = 0; i < mask.size(); i++) {
if (mask.get(i) != null) {
beforeDrawSlot(focused, mask.get(i), getSlotRect(i));
}
for (T item : getMask()) {
item.drawBorder(getSlotRect(item.slotIndex));
}
}
private void afterDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) {
final List<Integer> mask = getMask();
for (int i = 0; i < mask.size(); i++) {
if (mask.get(i) != null) {
afterDrawSlot(focused, mask.get(i), getSlotRect(i));
}
protected void afterDrawItems(int mousex, int mousey, M mouseContext) {
for (T item : getMask()) {
item.afterDraw(getSlotRect(item.slotIndex), mouseContext);
}
}
public void update() {}
protected void beforeDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {
if (focused != null && focused.slotIndex == slotIdx) {
drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555); // highlight
}
}
protected void drawItems() {
protected void afterDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {}
private void blitExistingBuffer() {
Minecraft minecraft = Minecraft.getMinecraft();
GL11.glEnable(GL11.GL_BLEND);
OpenGlHelper.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
getFrameBuffer().bindFramebufferTexture();
GL11.glEnable(GL11.GL_TEXTURE_2D);
ScaledResolution res = new ScaledResolution(minecraft, minecraft.displayWidth, minecraft.displayHeight);
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0, res.getScaledHeight_double(), 0.0, 0, 0);
tessellator.addVertexWithUV(res.getScaledWidth_double(), res.getScaledHeight_double(), 0.0, 1, 0);
tessellator.addVertexWithUV(res.getScaledWidth_double(), 0, 0.0, 1, 1);
tessellator.addVertexWithUV(0, 0, 0, 0, 1);
tessellator.draw();
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
}
private Framebuffer getFrameBuffer() {
if (framebuffer == null) {
framebuffer = new Framebuffer(1, 1, true);
framebuffer.setFramebufferColor(0.0F, 0.0F, 0.0F, 0.0F);
for (T item : getMask()) {
item.drawItem(getSlotRect(item.slotIndex));
}
return framebuffer;
}
private void drawItems() {
GuiContainerManager.enableMatrixStackLogging();
final List<Integer> mask = getMask();
for (int i = 0; i < mask.size(); i++) {
if (mask.get(i) != null) {
drawItem(getSlotRect(i), mask.get(i));
}
}
GuiContainerManager.disableMatrixStackLogging();
}
protected boolean shouldCacheItemRendering() {
return NEIClientConfig.shouldCacheItemRendering();
protected int getGridRenderingCacheMode() {
return NEIClientConfig.getGridRenderingCacheMode();
}
public void draw(int mousex, int mousey) {
@@ -328,77 +545,33 @@ public class ItemsGrid {
return;
}
final ItemPanelSlot focused = getSlotMouseOver(mousex, mousey);
final int gridRenderingCacheMode = getGridRenderingCacheMode();
final M mouseContext = getMouseContext(mousex, mousey);
if (shouldCacheItemRendering()) {
beforeDrawItems(mousex, mousey, mouseContext);
if (refreshBuffer) {
Minecraft minecraft = Minecraft.getMinecraft();
Framebuffer framebuffer = getFrameBuffer();
framebuffer.createBindFramebuffer(minecraft.displayWidth, minecraft.displayHeight);
framebuffer.framebufferClear();
framebuffer.bindFramebuffer(false);
if (gridRenderingCacheMode > 0) {
/* Set up some rendering state needed for items to work correctly */
GL11.glDisable(GL11.GL_BLEND);
GL11.glDepthMask(true);
OpenGlHelper.glBlendFunc(
GL11.GL_SRC_ALPHA,
GL11.GL_ONE_MINUS_SRC_ALPHA,
GL11.GL_SRC_ALPHA,
GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
drawItems();
Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(false);
refreshBuffer = false;
if (this.screenCapture == null) {
this.screenCapture = new ScreenCapture();
}
beforeDrawItems(mousex, mousey, focused);
blitExistingBuffer();
if (this.screenCapture.needRefresh(gridRenderingCacheMode)) {
this.screenCapture.captureScreen(() -> {
GuiContainerManager.enableMatrixStackLogging();
drawItems();
GuiContainerManager.disableMatrixStackLogging();
}, gridRenderingCacheMode);
}
this.screenCapture.renderCapturedScreen();
} else {
beforeDrawItems(mousex, mousey, focused);
GuiContainerManager.enableMatrixStackLogging();
drawItems();
GuiContainerManager.disableMatrixStackLogging();
}
afterDrawItems(mousex, mousey, focused);
}
public void postDrawTooltips(int mousex, int mousey, List<String> tooltip) {}
public void setVisible() {
if (getItems().isEmpty() && getMessageOnEmpty() != null) {
LayoutManager.addWidget(messageLabel);
}
}
protected void drawItem(Rectangle4i rect, int idx) {
GuiContainerManager.drawItem(rect.x + 1, rect.y + 1, getItem(idx));
}
public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
if (!contains(mousex, mousey)) {
return null;
}
final int overRow = (int) ((mousey - marginTop) / SLOT_SIZE);
final int overColumn = (int) ((mousex - marginLeft - paddingLeft) / SLOT_SIZE);
final int slt = columns * overRow + overColumn;
if (overRow >= rows || overColumn >= columns) {
return null;
}
final List<Integer> mask = getMask();
if (mask.size() > slt && mask.get(slt) != null) {
final int idx = mask.get(slt);
return new ItemPanelSlot(idx, getItem(idx));
}
return null;
afterDrawItems(mousex, mousey, mouseContext);
}
public boolean contains(int px, int py) {
@@ -412,13 +585,7 @@ public class ItemsGrid {
return false;
}
final int r = (int) ((py - marginTop) / SLOT_SIZE);
final int c = (int) ((px - marginLeft - paddingLeft) / SLOT_SIZE);
return !isInvalidSlot(columns * r + c);
return !isInvalidSlot(columns * getRowIndex(py) + getColumnIndex(px));
}
public String getMessageOnEmpty() {
return null;
}
}
@@ -0,0 +1,259 @@
package codechicken.nei;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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;
import codechicken.nei.recipe.Recipe.RecipeId;
public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot, ItemsPanelGrid.MouseContext> {
public static class ItemsPanelGridSlot extends ItemsGridSlot {
protected static final float LINE_WIDTH = 0.75f;
public int groupIndex = -1;
public String displayName = "";
public ItemStack bgItemStack;
public int groupSize = 1;
public boolean extended = false;
public Color bgColor = null;
public ItemsPanelGridSlot(int slotIndex, int itemIndex, ItemStack itemStack) {
super(slotIndex, itemIndex, itemStack);
}
public ItemStack getBGItem() {
return this.bgItemStack;
}
@Override
public RecipeId getRecipeId() {
return FavoriteRecipes.getFavorite(this.item);
}
@Override
public void beforeDraw(Rectangle4i rect, MouseContext mouseContext) {
super.beforeDraw(rect, mouseContext);
if (this.bgColor != null) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, this.bgColor);
}
}
@Override
public void drawItem(Rectangle4i rect) {
if (!this.extended && getBGItem() != null) {
GuiContainerManager.drawItems.zLevel -= 10F;
super.drawItem(getBGItem(), rect.offset(1, -1));
GuiContainerManager.drawItems.zLevel += 10F;
super.drawItem(getItemStack(), rect.offset(-2, 2));
} else {
super.drawItem(rect);
}
}
}
public ArrayList<ItemStack> newItems;
public ArrayList<ItemStack> rawItems = new ArrayList<>();
protected List<ItemsPanelGridSlot> gridMask;
protected final Map<Integer, Integer> itemToGroup = new HashMap<>();
protected final Map<Integer, List<ItemStack>> groupItems = new HashMap<>();
protected boolean forceExpand = false;
public void setItems(ArrayList<ItemStack> items) {
newItems = items;
}
public static void updateScale() {
SLOT_SIZE = 2 + (int) Math.ceil(8f * NEIClientConfig.getIntSetting("inventory.itempanelScale") / 100) * 2;
}
@Override
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;
if (!CollapsibleItems.isEmpty() && !this.newItems.isEmpty()) {
final Set<Integer> groups = new HashSet<>();
boolean outsideGroup = false;
this.realItems = new ArrayList<>();
this.rawItems = new ArrayList<>(this.newItems);
for (ItemStack stack : this.newItems) {
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
if (groupIndex == -1) {
this.realItems.add(stack);
outsideGroup = true;
} else {
if (!groups.contains(groupIndex) || CollapsibleItems.isExpanded(groupIndex)) {
this.itemToGroup.put(this.realItems.size(), groupIndex);
this.realItems.add(stack);
groups.add(groupIndex);
}
this.groupItems.computeIfAbsent(groupIndex, gi -> new ArrayList<>()).add(stack);
}
}
// automatically opens if there are elements from only one group
if (!outsideGroup && groups.size() == 1) {
final int groupIndex = groups.iterator().next();
this.realItems = new ArrayList<>(this.newItems);
this.itemToGroup.clear();
this.forceExpand = true;
for (int itemIndex = 0; itemIndex < this.realItems.size(); itemIndex++) {
this.itemToGroup.put(itemIndex, groupIndex);
}
}
} else {
this.realItems = new ArrayList<>(this.newItems);
}
this.newItems = null;
onItemsChanged();
}
super.refresh(gui);
}
@Override
protected void onGridChanged() {
this.gridMask = null;
super.onGridChanged();
}
@Override
public List<ItemsPanelGridSlot> getMask() {
if (this.gridMask == null) {
this.gridMask = new ArrayList<>();
int itemIndex = page * perPage;
for (int slotIndex = 0; slotIndex < rows * columns && itemIndex < size(); slotIndex++) {
if (!isInvalidSlot(slotIndex)) {
this.gridMask.add(new ItemsPanelGridSlot(slotIndex, itemIndex, getItem(itemIndex)));
itemIndex++;
}
}
calculateGroupBorders(this.gridMask);
}
return this.gridMask;
}
@Override
protected MouseContext getMouseContext(int mousex, int mousey) {
final ItemsPanelGridSlot hovered = getSlotMouseOver(mousex, mousey);
if (hovered != null) {
return new MouseContext(
hovered.slotIndex,
hovered.slotIndex / this.columns,
hovered.slotIndex % this.columns);
}
return null;
}
protected void calculateGroupBorders(List<ItemsPanelGridSlot> gridMask) {
if (gridMask.isEmpty() || this.groupItems.isEmpty()) {
return;
}
final Color collapsedBGColor = new Color(
NEIClientConfig.getSetting("inventory.collapsibleItems.collapsedColor").getHexValue(),
true);
final Color expandedBGColor = new Color(
NEIClientConfig.getSetting("inventory.collapsibleItems.expandedColor").getHexValue(),
true);
final Color collapsedBorderColor = darkerColor(collapsedBGColor);
final Color expandedBorderColor = darkerColor(expandedBGColor);
final Map<Integer, Integer> borderGrid = new HashMap<>();
for (ItemsPanelGridSlot item : gridMask) {
borderGrid.put(item.slotIndex, this.itemToGroup.getOrDefault(item.itemIndex, -1));
}
for (ItemsPanelGridSlot item : gridMask) {
final int groupIndex = borderGrid.get(item.slotIndex);
final List<ItemStack> groupItems = this.groupItems.get(groupIndex);
if (groupItems == null || groupItems.size() == 1) {
continue;
}
int column = item.slotIndex % this.columns;
int row = item.slotIndex / this.columns;
int prevSlotIndex = (row - 1) * this.columns + column;
int nextSlotIndex = (row + 1) * this.columns + column;
item.groupIndex = groupIndex;
item.groupSize = groupItems.size();
item.bgItemStack = groupItems.get(groupItems.size() - 1);
item.displayName = CollapsibleItems.getDisplayName(groupIndex);
item.extended = this.forceExpand || CollapsibleItems.isExpanded(groupIndex);
item.bgColor = item.extended ? expandedBGColor : collapsedBGColor;
item.borderColor = item.extended ? expandedBorderColor : collapsedBorderColor;
item.borderLeft = column == 0 || borderGrid.getOrDefault(item.slotIndex - 1, -1) != groupIndex;
item.borderRight = column + 1 == this.columns
|| borderGrid.getOrDefault(item.slotIndex + 1, -1) != groupIndex;
item.borderTop = borderGrid.getOrDefault(prevSlotIndex, -1) != groupIndex;
item.borderBottom = borderGrid.getOrDefault(nextSlotIndex, -1) != groupIndex;
}
}
private static Color darkerColor(Color color) {
return new Color(
color.getRed(),
color.getGreen(),
color.getBlue(),
Math.min((int) (color.getAlpha() + (255f / 5) * 2), 255));
}
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);
}
}
}
@@ -0,0 +1,410 @@
package codechicken.nei;
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;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import org.lwjgl.opengl.GL11;
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 {
@FunctionalInterface
public interface AmountRenderer {
void draw(int x, int y, ItemStack stack, long amount);
}
public static class NoAmountRenderer implements AmountRenderer {
@Override
public void draw(int x, int y, ItemStack stack, long amount) {
GuiContainerManager.drawItem(x, y, stack, true, "");
}
protected static void drawAmountText(String text, Rectangle4i rect, int color, Alignment alignment) {
NEIClientUtils.drawNEIOverlayText(text, rect, 1, color, true, alignment);
}
}
public static class TotalAmountRenderer extends NoAmountRenderer {
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 -> NEIClientUtils.areStacksSameType(stack, activeStack))
.findFirst().orElse(null);
this.activeStackIndex = items.indexOf(realStack);
}
public ItemStack getActiveStack() {
return this.activeStackIndex == -1 ? null : this.items.get(this.activeStackIndex);
}
public List<ItemStack> getItems() {
return this.items;
}
public boolean isEmpty() {
return this.items.isEmpty();
}
@Override
public Dimension getSize() {
return this.size;
}
public void setLabelColor(EnumChatFormatting color) {
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;
y += MARGIN_TOP;
fontRenderer.drawStringWithShadow(this.labelColor + this.label + ":", x, y, 0);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
RenderHelper.enableGUIStandardItemLighting();
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.gridCount; index++) {
drawSlot(
(index % this.gridColumns) * SLOT_SIZE,
(this.nameRows + index / this.gridColumns) * SLOT_SIZE,
this.nameRows + gridShift + index);
}
NEIClientUtils.gl2DRenderContext(() -> {
final int textShift = ICON_OFFSET + Math.round((ICON_SIZE - fontRenderer.FONT_HEIGHT) / 2f);
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);
}
final int hidden = this.length - this.nameRows - this.gridCount;
if (hidden > 0) {
final String text = "+" + hidden;
fontRenderer.drawStringWithShadow(
EnumChatFormatting.GRAY + text,
this.size.width - fontRenderer.getStringWidth(text) - 2,
(this.rows - 1) * SLOT_SIZE + textShift,
0xFFFFFF);
}
});
GL11.glTranslatef(-xTranslation, -yTranslation, -zTranslation);
GL11.glPopAttrib();
}
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()) {
result.add(StackInfo.loadFromNBT(entry.getKey(), 0));
this.amounts.add(Math.max(0, entry.getValue()));
}
if (result.isEmpty()) {
for (ItemStack stack : items) {
result.add(stack);
this.amounts.add(0L);
}
}
return result;
}
}
+175 -20
View File
@@ -2,10 +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 {
@@ -14,30 +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 boolean down = NEIClientConfig.isKeyHashDown(entry.getKey());
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();
@@ -0,0 +1,21 @@
package codechicken.nei;
import java.util.LinkedHashMap;
import java.util.Map;
// Simple LRUCache
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
}
+233 -121
View File
@@ -31,8 +31,11 @@ import static codechicken.nei.NEIClientUtils.toggleMagnetMode;
import static codechicken.nei.NEIClientUtils.toggleRaining;
import static codechicken.nei.NEIClientUtils.translate;
import java.awt.Point;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import net.minecraft.client.Minecraft;
@@ -40,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;
@@ -62,8 +66,10 @@ import codechicken.nei.guihook.IContainerDrawHandler;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.guihook.IContainerObjectHandler;
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 {
@@ -76,34 +82,29 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
/**
* Sorted bottom first
*/
private static TreeSet<Widget> drawWidgets = new TreeSet<>(new WidgetZOrder(false));
private static Set<Widget> drawWidgets = new TreeSet<>(new WidgetZOrder(false));
/**
* Sorted top first
*/
private static TreeSet<Widget> controlWidgets = new TreeSet<>(new WidgetZOrder(true));
private static Set<Widget> controlWidgets = new TreeSet<>(new WidgetZOrder(true));
public static ItemPanel itemPanel;
public static BookmarkPanel bookmarkPanel;
public static SubsetWidget dropDown;
public static PresetsWidget presetsPanel;
public static SearchField searchField;
public static ItemZoom itemZoom;
public static ButtonCycled options;
public static ButtonCycled bookmarksButton;
@Deprecated
public static Button more;
@Deprecated
public static Button less;
@Deprecated
public static ItemQuantityField quantity;
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;
@@ -118,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());
@@ -137,16 +133,14 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public void onPreDraw(GuiContainer gui) {
if (!isHidden() && isEnabled() && gui instanceof InventoryEffectRenderer) // Reset the gui to the center of the
// screen, for potion effect offsets
// etc
{
// Reset the gui to the center of the screen, for potion effect offsets etc
if (isEnabled() && gui instanceof InventoryEffectRenderer) {
gui.guiLeft = (gui.width - gui.xSize) / 2;
gui.guiTop = (gui.height - gui.ySize) / 2;
if (gui instanceof GuiContainerCreative && gui.buttonList.size() >= 2) {
GuiButton button1 = (GuiButton) gui.buttonList.get(0);
GuiButton button2 = (GuiButton) gui.buttonList.get(1);
GuiButton button1 = gui.buttonList.get(0);
GuiButton button2 = gui.buttonList.get(1);
button1.xPosition = gui.guiLeft;
button2.xPosition = gui.guiLeft + gui.xSize - 20;
}
@@ -167,14 +161,18 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
if (isHidden()) return;
for (Widget widget : controlWidgets) widget.onGuiClick(mousex, mousey);
for (Widget widget : controlWidgets) {
widget.onGuiClick(mousex, mousey);
}
}
@Override
public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
if (isHidden()) return false;
if (!isEnabled()) return options.contains(mousex, mousey) && options.handleClick(mousex, mousey, button);
if (!isEnabled()) {
return options.contains(mousex, mousey) && options.handleClick(mousex, mousey, button);
}
for (Widget widget : controlWidgets) {
widget.onGuiClick(mousex, mousey);
@@ -188,10 +186,22 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean objectUnderMouse(GuiContainer gui, int mousex, int mousey) {
if (!isHidden() && isEnabled())
for (Widget widget : drawWidgets) if (widget.contains(mousex, mousey)) return true;
return getWidgetUnderMouse(mousex, mousey) != null;
}
return false;
public Widget getWidgetUnderMouse(int mousex, int mousey) {
if (isHidden() || !isEnabled()) {
return null;
}
for (Widget widget : controlWidgets) {
if (widget.contains(mousex, mousey)) {
return widget;
}
}
return null;
}
public boolean keyTyped(GuiContainer gui, char keyChar, int keyID) {
@@ -210,23 +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")) {
toggleBooleanSetting("inventory.bookmarksEnabled");
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;
@@ -247,12 +274,12 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public ItemStack getStackUnderMouse(GuiContainer gui, int mousex, int mousey) {
if (!isHidden() && isEnabled()) {
for (Widget widget : drawWidgets) {
ItemStack stack = widget.getStackMouseOver(mousex, mousey);
if (stack != null) return stack;
}
final Widget focused = getWidgetUnderMouse(mousex, mousey);
if (focused != null) {
return focused.getStackMouseOver(mousex, mousey);
}
return null;
}
@@ -279,18 +306,21 @@ 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)) {
for (Widget widget : drawWidgets) widget.postDrawTooltips(mousex, mousey, tooltip);
}
}
@Override
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
for (Widget widget : drawWidgets) currenttip = widget.handleTooltip(mousex, mousey, currenttip);
final Widget focused = getWidgetUnderMouse(mousex, mousey);
if (focused != null) {
currenttip = focused.handleTooltip(mousex, mousey, currenttip);
if (focused instanceof IContainerTooltipHandler tip) {
currenttip = tip.handleTooltip(gui, mousex, mousey, currenttip);
}
}
}
return currenttip;
}
@@ -307,32 +337,64 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
currenttip.set(0, mainname);
}
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
final Point mouse = GuiDraw.getMousePosition();
final Widget focused = getWidgetUnderMouse(mouse.x, mouse.y);
if (focused instanceof IContainerTooltipHandler tip) {
currenttip = tip.handleItemDisplayName(gui, stack, currenttip);
}
}
return currenttip;
}
@Override
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
List<String> currenttip) {
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);
}
}
return currenttip;
}
@Override
public Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> hotkeys) {
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
final Widget focused = getWidgetUnderMouse(mousex, mousey);
if (focused != null) {
hotkeys = focused.handleHotkeys(mousex, mousey, hotkeys);
if (focused instanceof IContainerTooltipHandler tip) {
hotkeys = tip.handleHotkeys(gui, mousex, mousey, hotkeys);
}
}
}
return hotkeys;
}
public static void layout(GuiContainer gui) {
VisiblityData visiblity = new VisiblityData();
if (isHidden()) visiblity.showNEI = false;
if (isBookmarkPanelHidden()) visiblity.showBookmarkPanel = false;
if (gui.height - gui.ySize <= 40 && NEIClientConfig.isSearchWidgetCentered())
visiblity.showSearchSection = false;
if (visiblity.showBookmarkPanel || gui.guiTop <= 20) visiblity.showSubsetDropdown = false;
if (!visiblity.showBookmarkPanel || gui.guiTop <= 20) visiblity.showPresetsDropdown = false;
if (gui.guiLeft - 4 < 76) visiblity.showWidgets = false;
if (!itemsLoaded) visiblity.showPresetsDropdown = false;
visiblity.showNEI = !isHidden();
visiblity.showBookmarkPanel = !isBookmarkPanelHidden();
visiblity.showSearchSection = !(gui.height - gui.ySize <= 40 && NEIClientConfig.isSearchWidgetCentered());
visiblity.showSubsetDropdown = NEIClientConfig.showSubsetWidget()
&& (gui.guiTop > 20 || visiblity.showSearchSection && !NEIClientConfig.subsetWidgetOnTop());
visiblity.showWidgets = gui.guiLeft - 4 > 76;
try {
GuiInfo.readLock.lock();
@@ -355,9 +417,11 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
bookmarkPanel = ItemPanels.bookmarkPanel;
bookmarkPanel.init();
itemZoom = new ItemZoom();
dropDown = new SubsetWidget();
presetsPanel = new PresetsWidget();
searchField = new SearchField("search");
API.addItemFilter(searchField);
options = new ButtonCycled(3) {
@@ -379,7 +443,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean onButtonPress(boolean rightclick) {
if (!rightclick) {
if (Keyboard.getEventKeyState() && getIsAccessibleControlEventKey()) {
if (Keyboard.getEventKeyState() && getIsAccessibleControlEventKey()
&& NEIClientConfig.canChangeCheatMode()) {
NEIClientConfig.cycleSetting("inventory.cheatmode", 3);
} else {
if (Keyboard.getEventKeyState() && (Keyboard.getEventKey() == Keyboard.KEY_LSHIFT
@@ -403,7 +468,10 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
else if (cheatMode == 2) modeColor = EnumChatFormatting.RED.toString();
String controlKeyLocalization = translate(Minecraft.isRunningOnMac ? "key.ctrl.mac" : "key.ctrl");
tooltip.add(modeColor + translate("inventory.options.tip.cheatmode." + cheatMode));
tooltip.add(modeColor + translate("inventory.options.tip.cheatmode.disable", controlKeyLocalization));
if (NEIClientConfig.canChangeCheatMode()) {
tooltip.add(
modeColor + translate("inventory.options.tip.cheatmode.disable", controlKeyLocalization));
}
}
@Override
@@ -422,7 +490,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean onButtonPress(boolean rightclick) {
NEIClientConfig.toggleBooleanSetting("inventory.bookmarksEnabled");
NEIClientConfig.toggleBooleanSetting("inventory.bookmarks.enabled");
return true;
}
@@ -436,9 +504,43 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
}
};
more = ItemPanels.itemPanel.more;
less = ItemPanels.itemPanel.less;
quantity = ItemPanels.itemPanel.quantity;
more = new Button("+") {
@Override
public boolean onButtonPress(boolean rightclick) {
if (rightclick) return false;
int modifier = NEIClientUtils.controlKey() ? 64 : NEIClientUtils.shiftKey() ? 10 : 1;
int quantity = NEIClientConfig.getItemQuantity() + modifier;
if (quantity < 0) {
quantity = 0;
}
LayoutManager.quantity.setText(Integer.toString(quantity));
return true;
}
};
less = new Button("-") {
@Override
public boolean onButtonPress(boolean rightclick) {
if (rightclick) return false;
int modifier = NEIClientUtils.controlKey() ? -64 : NEIClientUtils.shiftKey() ? -10 : -1;
int quantity = NEIClientConfig.getItemQuantity() + modifier;
if (quantity < 0) {
quantity = 0;
}
LayoutManager.quantity.setText(Integer.toString(quantity));
return true;
}
};
quantity = new ItemQuantityField("quantity");
delete = new Button() {
@Override
@@ -509,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) {
@@ -614,12 +716,12 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (!itemsLoaded) {
ItemList.loadItems.restart();
itemsLoaded = true;
}
overlayRenderer = null;
overlayRenderer = null;
getLayoutStyle().init();
layout(gui);
AutoFocusWidget.instance.load(gui);
}
NEIController.load(gui);
@@ -651,14 +753,15 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
addWidget(options);
addWidget(bookmarksButton);
if (visiblity.showItemPanel && NEIClientConfig.showItemQuantityWidget()) {
addWidget(quantity);
addWidget(more);
addWidget(less);
}
if (visiblity.showItemPanel) {
if (PresetsWidget.inEditMode()) {
drawWidgets.add(itemPanel);
} else {
addWidget(itemPanel);
}
addWidget(itemPanel);
itemPanel.setVisible();
}
@@ -667,13 +770,13 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
bookmarkPanel.setVisible();
}
if (visiblity.showPresetsDropdown) {
addWidget(presetsPanel);
}
searchField.setVisible(visiblity.showSearchSection);
if (visiblity.showSearchSection) {
if (visiblity.showSearchSection && NEIClientConfig.isSearchWidgetCentered()
|| visiblity.showItemPanel && !NEIClientConfig.isSearchWidgetCentered()) {
searchField.setVisible(true);
addWidget(searchField);
} else {
searchField.setVisible(false);
searchField.setFocus(false);
}
if (visiblity.showUtilityButtons) {
@@ -688,10 +791,13 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (canPerformAction("delete")) addWidget(delete);
}
addWidget(itemZoom);
if (visiblity.showSubsetDropdown) {
// Bookmarks or Subset/dropdown
addWidget(dropDown);
}
addWidget(DebugHandlerWidget.instance);
}
public static LayoutStyle getLayoutStyle(int id) {
@@ -715,6 +821,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
if (!isEnabled()) return;
AutoFocusWidget.instance.guiTick();
for (Widget widget : drawWidgets) widget.update();
}
@@ -722,7 +830,9 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
if (isHidden() || !isEnabled()) return false;
for (Widget widget : drawWidgets) if (widget.onMouseWheel(scrolled, mousex, mousey)) return true;
for (Widget widget : controlWidgets) {
if (widget.onMouseWheel(scrolled, mousex, mousey)) return true;
}
return false;
}
@@ -732,7 +842,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean shouldShowTooltip(GuiContainer gui) {
return itemPanel.draggedStack == null && !bookmarkPanel.inEditingState();
return itemPanel.draggedStack == null && !bookmarkPanel.inEditingState() && !AutoCraftingManager.processing();
}
/** Note: this method isn't actually used by this mod, but NEI add-ons might need it. */
@@ -760,13 +870,11 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
ItemStack item = slot.getStack();
if (item == null ? !getSearchExpression().equals("") : !searchField.getFilter().matches(item)) {
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glTranslatef(0, 0, 150);
drawRect(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, 0x80000000);
GL11.glTranslatef(0, 0, -150);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
NEIClientUtils.gl2DRenderContext(() -> {
GL11.glTranslatef(0, 0, 150);
drawRect(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, 0x80000000);
GL11.glTranslatef(0, 0, -150);
});
}
}
@@ -811,35 +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) {
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glTranslatef(0, 0, -150);
drawRect(slotX, slotY, 16, 16, isPresent ? 0x8000AA00 : 0x80AA0000);
GL11.glTranslatef(0, 0, 150);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
} 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() {
@@ -850,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);
}
}
@@ -1,34 +1,19 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.drawStringC;
import static codechicken.nei.LayoutManager.bookmarkPanel;
import static codechicken.nei.LayoutManager.bookmarksButton;
import static codechicken.nei.LayoutManager.delete;
import static codechicken.nei.LayoutManager.dropDown;
import static codechicken.nei.LayoutManager.gamemode;
import static codechicken.nei.LayoutManager.heal;
import static codechicken.nei.LayoutManager.itemPanel;
import static codechicken.nei.LayoutManager.itemPresenceOverlays;
import static codechicken.nei.LayoutManager.magnet;
import static codechicken.nei.LayoutManager.options;
import static codechicken.nei.LayoutManager.presetsPanel;
import static codechicken.nei.LayoutManager.rain;
import static codechicken.nei.LayoutManager.searchField;
import static codechicken.nei.LayoutManager.timeButtons;
import static codechicken.nei.NEIClientConfig.canPerformAction;
import static codechicken.nei.NEIClientConfig.disabledActions;
import static codechicken.nei.NEIClientConfig.getMagnetMode;
import static codechicken.nei.NEIClientConfig.isEnabled;
import net.minecraft.client.gui.inventory.GuiContainer;
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 {
protected static final int BUTTON_SIZE = 20;
protected static final int MARGIN = 2;
public int buttonCount;
public int leftSize;
public int numButtons;
@@ -40,19 +25,18 @@ public class LayoutStyleMinecraft extends LayoutStyle {
@Override
public void init() {
delete.icon = new Image(144, 12, 12, 12);
rain.icon = new Image(120, 12, 12, 12);
gamemode.icons[0] = new Image(132, 12, 12, 12);
gamemode.icons[1] = new Image(156, 12, 12, 12);
gamemode.icons[2] = new Image(168, 12, 12, 12);
magnet.icon = new Image(180, 24, 12, 12);
timeButtons[0].icon = new Image(132, 24, 12, 12);
timeButtons[1].icon = new Image(120, 24, 12, 12);
timeButtons[2].icon = new Image(144, 24, 12, 12);
timeButtons[3].icon = new Image(156, 24, 12, 12);
heal.icon = new Image(168, 24, 12, 12);
itemPresenceOverlays[0] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 0, 40, 8, 8).build();
itemPresenceOverlays[1] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 8, 40, 8, 8).build();
LayoutManager.delete.icon = new Image(144, 12, 12, 12);
LayoutManager.rain.icon = new Image(120, 12, 12, 12);
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.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);
}
@Override
@@ -64,90 +48,179 @@ public class LayoutStyleMinecraft extends LayoutStyle {
public void layout(GuiContainer gui, VisiblityData visiblity) {
reset();
leftSize = ItemPanels.bookmarkPanel.getWidth(gui);
numButtons = Math.max(leftSize / 18, 1);
leftSize = (gui.width - gui.xSize) / 2 - 4;
numButtons = Math.max(leftSize / 20, 1);
delete.state = 0x4;
if (NEIController.getDeleteMode()) delete.state |= 1;
else if (!visiblity.enableDeleteMode) delete.state |= 2;
rain.state = 0x4;
if (disabledActions.contains("rain")) rain.state |= 2;
else if (NEIClientUtils.isRaining()) rain.state |= 1;
gamemode.state = 0x4;
if (NEIClientUtils.getGamemode() != 0) {
gamemode.state |= 0x1;
gamemode.index = NEIClientUtils.getGamemode() - 1;
} else {
if (NEIClientUtils.isValidGamemode("creative")) gamemode.index = 0;
else if (NEIClientUtils.isValidGamemode("creative+")) gamemode.index = 1;
else if (NEIClientUtils.isValidGamemode("adventure")) gamemode.index = 2;
LayoutManager.delete.state = 0x4;
if (NEIController.getDeleteMode()) {
LayoutManager.delete.state |= 1;
} else if (!visiblity.enableDeleteMode) {
LayoutManager.delete.state |= 2;
}
bookmarksButton.index = NEIClientConfig.isBookmarkPanelHidden() ? 0 : 1;
options.index = NEIClientConfig.getCheatMode();
magnet.state = 0x4 | (getMagnetMode() ? 1 : 0);
LayoutManager.rain.state = 0x4;
if (NEIClientConfig.disabledActions.contains("rain")) {
LayoutManager.rain.state |= 2;
} else if (NEIClientUtils.isRaining()) {
LayoutManager.rain.state |= 1;
}
if (canPerformAction("delete")) layoutButton(delete);
if (canPerformAction("rain")) layoutButton(rain);
if (NEIClientUtils.isValidGamemode("creative") || NEIClientUtils.isValidGamemode("creative+")
|| NEIClientUtils.isValidGamemode("adventure"))
layoutButton(gamemode);
if (canPerformAction("magnet")) layoutButton(magnet);
if (canPerformAction("time")) {
for (int i = 0; i < 4; i++) {
timeButtons[i].state = disabledActions.contains(NEIActions.timeZones[i]) ? 2 : 0;
layoutButton(timeButtons[i]);
LayoutManager.gamemode.state = 0x4;
if (NEIClientUtils.getGamemode() != 0) {
LayoutManager.gamemode.state |= 0x1;
LayoutManager.gamemode.index = NEIClientUtils.getGamemode() - 1;
} else {
if (NEIClientUtils.isValidGamemode("creative")) {
LayoutManager.gamemode.index = 0;
} else if (NEIClientUtils.isValidGamemode("creative+")) {
LayoutManager.gamemode.index = 1;
} else if (NEIClientUtils.isValidGamemode("adventure")) {
LayoutManager.gamemode.index = 2;
}
}
if (canPerformAction("heal")) layoutButton(heal);
LayoutManager.bookmarksButton.index = NEIClientConfig.isBookmarkPanelHidden() ? 0 : 1;
LayoutManager.options.index = NEIClientConfig.getCheatMode();
itemPanel.resize(gui);
bookmarkPanel.resize(gui);
LayoutManager.magnet.state = 0x4 | (NEIClientConfig.getMagnetMode() ? 1 : 0);
LayoutManager.magnet.index = (LayoutManager.magnet.state & 0x1) != 0 ? 1 : 0;
options.x = isEnabled() ? 0 : 6;
options.y = isEnabled() ? gui.height - 22 : gui.height - 28;
options.w = 22;
options.h = 22;
if (NEIClientConfig.canPerformAction("delete")) {
layoutButton(LayoutManager.delete);
}
bookmarksButton.x = 24 + (isEnabled() ? 0 : 6);
bookmarksButton.y = isEnabled() ? gui.height - 22 : gui.height - 28;
bookmarksButton.w = 22;
bookmarksButton.h = 22;
if (NEIClientConfig.canPerformAction("rain")) {
layoutButton(LayoutManager.rain);
}
dropDown.h = 18;
dropDown.w = 150;
dropDown.y = 0;
dropDown.x = (gui.width - gui.xSize) / 2 + gui.xSize - dropDown.w;
if (NEIClientUtils.isValidGamemode("creative") || NEIClientUtils.isValidGamemode("creative+")
|| NEIClientUtils.isValidGamemode("adventure")) {
layoutButton(LayoutManager.gamemode);
}
presetsPanel.h = 16;
presetsPanel.w = 150;
presetsPanel.y = 2;
presetsPanel.x = (gui.width - gui.xSize) / 2 + gui.xSize - presetsPanel.w;
if (NEIClientConfig.canPerformAction("magnet")) {
layoutButton(LayoutManager.magnet);
}
searchField.h = 20;
if (NEIClientConfig.isSearchWidgetCentered()) {
searchField.w = 150;
searchField.x = (gui.width - searchField.w) / 2;
if (NEIClientConfig.canPerformAction("time")) {
for (int i = 0; i < 4; i++) {
LayoutManager.timeButtons[i].state = NEIClientConfig.disabledActions.contains(NEIActions.timeZones[i])
? 2
: 0;
layoutButton(LayoutManager.timeButtons[i]);
}
}
if (NEIClientConfig.canPerformAction("heal")) {
layoutButton(LayoutManager.heal);
}
layoutFooter(gui, visiblity);
LayoutManager.itemPanel.resize(gui);
LayoutManager.bookmarkPanel.resize(gui);
LayoutManager.itemZoom.resize(gui);
}
protected void layoutFooter(GuiContainer gui, VisiblityData visiblity) {
LayoutManager.options.x = MARGIN;
LayoutManager.options.y = gui.height - BUTTON_SIZE - MARGIN;
LayoutManager.options.w = LayoutManager.options.h = BUTTON_SIZE;
LayoutManager.bookmarksButton.x = LayoutManager.options.x + LayoutManager.options.w + MARGIN;
LayoutManager.bookmarksButton.y = gui.height - BUTTON_SIZE - MARGIN;
LayoutManager.bookmarksButton.w = LayoutManager.bookmarksButton.h = BUTTON_SIZE;
final Rectangle4i searchArea = getSearchFieldArea(gui);
final Rectangle4i quantityArea = getQuantityFieldArea(visiblity);
LayoutManager.searchField.x = searchArea.x;
LayoutManager.searchField.y = searchArea.y;
LayoutManager.searchField.w = searchArea.w;
LayoutManager.searchField.h = searchArea.h;
if (!NEIClientConfig.subsetWidgetOnTop() && visiblity.showSubsetDropdown) {
LayoutManager.dropDown.x = searchArea.x;
LayoutManager.dropDown.y = searchArea.y;
LayoutManager.dropDown.h = LayoutManager.dropDown.w = BUTTON_SIZE;
LayoutManager.searchField.x += BUTTON_SIZE;
LayoutManager.searchField.w -= BUTTON_SIZE;
} else {
searchField.w = (int) (itemPanel.w * (NEIClientConfig.showItemQuantityWidget() ? 0.7 : 1));
searchField.x = itemPanel.x;
LayoutManager.dropDown.h = 16;
LayoutManager.dropDown.w = 150;
LayoutManager.dropDown.y = MARGIN;
LayoutManager.dropDown.x = (gui.width - LayoutManager.dropDown.w) / 2;
}
searchField.y = gui.height - searchField.h - 2;
if (!visiblity.showItemSection) {
searchField.setFocus(false);
if (quantityArea.w == ItemPanels.itemPanel.w) {
LayoutManager.more.w = LayoutManager.less.w = BUTTON_SIZE;
LayoutManager.more.h = LayoutManager.less.h = LayoutManager.quantity.h = quantityArea.h;
LayoutManager.more.y = LayoutManager.less.y = LayoutManager.quantity.y = quantityArea.y;
LayoutManager.less.x = quantityArea.x;
LayoutManager.more.x = quantityArea.x2() - BUTTON_SIZE;
LayoutManager.quantity.x = LayoutManager.less.x + LayoutManager.less.w + 1;
LayoutManager.quantity.w = LayoutManager.more.x - LayoutManager.quantity.x - 1;
} else {
LayoutManager.quantity.x = quantityArea.x;
LayoutManager.quantity.w = quantityArea.w - 18;
LayoutManager.quantity.y = LayoutManager.more.y = quantityArea.y;
LayoutManager.quantity.h = quantityArea.h;
LayoutManager.more.w = LayoutManager.less.w = 18;
LayoutManager.more.x = LayoutManager.less.x = LayoutManager.quantity.x + LayoutManager.quantity.w;
LayoutManager.more.h = LayoutManager.less.h = LayoutManager.quantity.h / 2;
LayoutManager.less.y = LayoutManager.more.y + LayoutManager.more.h;
}
}
private Rectangle4i getSearchFieldArea(GuiContainer gui) {
final Rectangle4i area = new Rectangle4i(0, 0, 0, BUTTON_SIZE);
if (NEIClientConfig.isSearchWidgetCentered()) {
area.w = Math.min(gui.xSize, 176) - MARGIN * 2;
area.x = (gui.width - area.w) / 2;
area.y = gui.height - BUTTON_SIZE - MARGIN;
} else if (NEIClientConfig.showItemQuantityWidget()) {
area.w = ItemPanels.itemPanel.w - (int) Math.max(BUTTON_SIZE * 2.5f, ItemPanels.itemPanel.w * 0.3f) - 1;
area.x = ItemPanels.itemPanel.x;
area.y = ItemPanels.itemPanel.y + ItemPanels.itemPanel.h - BUTTON_SIZE;
} else {
area.w = ItemPanels.itemPanel.w;
area.x = ItemPanels.itemPanel.x;
area.y = ItemPanels.itemPanel.y + ItemPanels.itemPanel.h - BUTTON_SIZE;
}
return area;
}
private Rectangle4i getQuantityFieldArea(VisiblityData visiblity) {
final Rectangle4i area = new Rectangle4i(0, 0, 0, BUTTON_SIZE);
if (!visiblity.showSearchSection || NEIClientConfig.isSearchWidgetCentered()) {
area.w = ItemPanels.itemPanel.w;
area.x = ItemPanels.itemPanel.x;
} else {
area.w = (int) Math.max(BUTTON_SIZE * 2.5f, ItemPanels.itemPanel.w * 0.3f);
area.x = ItemPanels.itemPanel.x + ItemPanels.itemPanel.w - area.w;
}
area.y = ItemPanels.itemPanel.y + ItemPanels.itemPanel.h - BUTTON_SIZE;
return area;
}
public void layoutButton(Button button) {
button.x = 2 + (buttonCount % numButtons) * 19;
button.y = 2 + (buttonCount / numButtons) * 18;
button.h = 17;
button.w = button.contentWidth() + 6;
button.x = MARGIN + (buttonCount % numButtons) * (button.w + MARGIN);
button.y = MARGIN + (buttonCount / numButtons) * (button.h + MARGIN);
buttonCount++;
}
@@ -166,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 {
@@ -181,9 +258,16 @@ public class LayoutStyleMinecraft extends LayoutStyle {
@Override
public void drawSubsetTag(String text, int x, int y, int w, int h, int state, boolean mouseover) {
if (state == 1) GL11.glColor4f(0.65F, 0.65F, 0.65F, 1.0F);
else GL11.glColor4f(1, 1, 1, 1);
if (state == 1) {
GL11.glColor4f(0.65F, 0.65F, 0.65F, 1.0F);
} else {
GL11.glColor4f(1, 1, 1, 1);
}
LayoutManager.drawButtonBackground(x, y, w, h, false, state == 0 ? 0 : 1);
if (text != null) drawStringC(text, x, y, w, h, state == 2 ? 0xFFE0E0E0 : 0xFFA0A0A0);
if (text != null) {
drawStringC(text, x, y, w, h, mouseover ? 0xFFFFFFA0 : (state == 2 ? 0xFFE0E0E0 : 0xFFA0A0A0));
}
}
}
@@ -54,6 +54,7 @@ public class NEIActions {
addAction("magnet", true);
addAction("enchant", true);
addAction("potion", true);
addAction("autocraft");
canDisable.add("dawn");
canDisable.add("noon");
+23
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,6 +203,12 @@ public class NEICPH implements IClientPacketHandler {
packet.sendToServer();
}
public static void sendChatLink(NBTTagCompound payloadTag) {
PacketCustom packet = new PacketCustom(channel, C2S.SEND_CHAT_ITEM_LINK);
packet.writeNBTTagCompound(payloadTag);
packet.sendToServer();
}
public static void sendOpenEnchantmentWindow() {
PacketCustom packet = new PacketCustom(channel, C2S.REQUEST_ENCHANTMENT_GUI);
packet.sendToServer();
File diff suppressed because it is too large. Load diff
+228 -64
View File
@@ -8,9 +8,9 @@ import static codechicken.nei.NEIClientConfig.hasSMPCounterPart;
import static codechicken.nei.NEIClientConfig.invCreativeMode;
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;
@@ -20,6 +20,7 @@ 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;
@@ -31,37 +32,77 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
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;
import codechicken.lib.inventory.InventoryUtils;
import codechicken.lib.util.LangProxy;
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;
public class NEIClientUtils extends NEIServerUtils {
public static class Alignment {
public final int x, y;
public static final Alignment TopLeft = new Alignment(-1, -1);
public static final Alignment TopCenter = new Alignment(0, -1);
public static final Alignment TopRight = new Alignment(1, -1);
public static final Alignment CenterLeft = new Alignment(-1, 0);
public static final Alignment Center = new Alignment(0, 0);
public static final Alignment CenterRight = new Alignment(1, 0);
public static final Alignment BottomLeft = new Alignment(-1, 1);
public static final Alignment BottomCenter = new Alignment(0, 1);
public static final Alignment BottomRight = new Alignment(1, 1);
public Alignment(int x, int y) {
this.x = x;
this.y = y;
}
public float getX(float parentWidth, float childWidth) {
final float x = (this.x + 1) / 2f;
return parentWidth * x - childWidth * x;
}
public float getY(float parentHeight, float childHeight) {
final float y = (this.y + 1) / 2f;
return parentHeight * y - childHeight * y;
}
}
public static LangProxy lang = new LangProxy("nei");
/** 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 = 1 << 27;
public static final int SHIFT_HASH = 1 << 26;
public static final int CTRL_HASH = 1 << 25;
public static final int ALT_HASH = NEIKeyboardUtils.ALT_HASH;
public static final int SHIFT_HASH = NEIKeyboardUtils.SHIFT_HASH;
public static final int CTRL_HASH = NEIKeyboardUtils.CTRL_HASH;
public static Minecraft mc() {
return Minecraft.getMinecraft();
@@ -89,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() {
@@ -161,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;
@@ -246,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);
}
@@ -308,7 +436,7 @@ public class NEIClientUtils extends NEIServerUtils {
}
public static void sendCommand(String command, Object... args) {
if (command.length() == 0) return;
if (command.isEmpty()) return;
NumberFormat numberformat = NumberFormat.getIntegerInstance();
numberformat.setGroupingUsed(false);
@@ -333,6 +461,10 @@ public class NEIClientUtils extends NEIServerUtils {
else sendCommand(getStringSetting("command.heal"), mc().thePlayer.getCommandSenderName());
}
public static void sendChatItemLink(NBTTagCompound payload) {
if (hasSMPCounterPart()) NEICPH.sendChatLink(payload);
}
public static void toggleMagnetMode() {
if (hasSMPCounterPart()) NEICPH.sendToggleMagnetMode();
}
@@ -367,18 +499,16 @@ public class NEIClientUtils extends NEIServerUtils {
}
public static String cropText(FontRenderer fontRenderer, String text, int containerWidth) {
int textWidth = fontRenderer.getStringWidth(text);
if (textWidth > containerWidth) {
textWidth += fontRenderer.getStringWidth("...");
int dots = fontRenderer.getStringWidth("...");
while (textWidth > containerWidth) {
textWidth -= fontRenderer.getCharWidth(text.charAt(text.length() - 1));
text = text.substring(0, text.length() - 1);
if (containerWidth <= dots) {
return "...";
}
return text + "...";
return fontRenderer.trimStringToWidth(text, containerWidth - dots) + "...";
}
return text;
@@ -406,43 +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 (keycode != Keyboard.KEY_LSHIFT && keycode != Keyboard.KEY_RSHIFT
&& keycode != Keyboard.KEY_LCONTROL
&& keycode != Keyboard.KEY_RCONTROL
&& keycode != Keyboard.KEY_LMENU
&& keycode != Keyboard.KEY_RMENU) {
return getMetaHash() + keycode;
}
}
return Keyboard.CHAR_NONE;
public static String getKeyName(int keyBind, int mouseBind) {
return KeyManager.getKeyName(keyBind, mouseBind);
}
public static void playClickSound() {
@@ -456,16 +551,9 @@ public class NEIClientUtils extends NEIServerUtils {
String stackTrace = cause + sw;
if (buffer.contains(stackTrace)) return;
System.err.println("Error while rendering: " + cause);
System.err.println("Error while rendering: " + cause + " (" + e.getMessage() + ")");
e.printStackTrace();
buffer.add(stackTrace);
EntityPlayer player = Minecraft.getMinecraft().thePlayer;
if (player != null) {
IChatComponent chat = new ChatComponentTranslation("nei.chat.render.error");
chat.getChatStyle().setColor(EnumChatFormatting.RED);
player.addChatComponentMessage(chat);
}
}
public static void reportErrorBuffered(Throwable e, Set<String> buffer, ItemStack cause) {
@@ -475,4 +563,80 @@ public class NEIClientUtils extends NEIServerUtils {
reportErrorBuffered(e, buffer, "null");
}
}
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) {
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);
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
GL11.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
tessellator.startDrawingQuads();
tessellator.addVertex(left, top + height, 0.0D);
tessellator.addVertex(left + width, top + height, 0.0D);
tessellator.addVertex(left + width, top, 0.0D);
tessellator.addVertex(left, top, 0.0D);
tessellator.draw();
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,
Alignment alignment) {
final float screenScale = mc().currentScreen.width * 1f / mc().displayWidth;
final double smallTextScale = Math
.max(screenScale, (Math.max(scale, 1f) * (GuiDraw.fontRenderer.getUnicodeFlag() ? 3F / 4F : 1F / 2F)));
NEIClientUtils.gl2DRenderContext(() -> {
final int width = GuiDraw.fontRenderer.getStringWidth(text);
final float partW = rect.w / 2f;
final float partH = rect.h / 2f;
final double offsetX = Math
.ceil(rect.x + partW + partW * alignment.x - (width / 2f * (alignment.x + 1)) * smallTextScale);
final double offsetY = Math.ceil(
rect.y + partH
+ partH * alignment.y
- (GuiDraw.fontRenderer.FONT_HEIGHT / 2f * (alignment.y + 1)) * smallTextScale);
GL11.glTranslated(offsetX, offsetY, 0);
GL11.glScaled(smallTextScale, smallTextScale, 1);
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);
});
}
public static void drawNEIOverlayText(String text, int x, int y) {
drawNEIOverlayText(text, new Rectangle4i(x, y, 16, 16), 0.5f, 0xFDD835, false, Alignment.TopLeft);
}
public static void gl2DRenderContext(Runnable callback) {
boolean isLighting = GL11.glGetBoolean(GL11.GL_LIGHTING);
boolean isDepthTest = GL11.glGetBoolean(GL11.GL_DEPTH_TEST);
boolean isAlphaTest = GL11.glGetBoolean(GL11.GL_ALPHA_TEST);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glDisable(GL11.GL_DEPTH_TEST);
callback.run();
if (isLighting) GL11.glEnable(GL11.GL_LIGHTING);
if (isDepthTest) GL11.glEnable(GL11.GL_DEPTH_TEST);
if (!isAlphaTest) GL11.glDisable(GL11.GL_ALPHA_TEST);
}
}
@@ -1,26 +1,35 @@
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;
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;
public class NEIController implements IContainerSlotClickHandler, IContainerInputHandler {
@@ -39,6 +48,8 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
public static void load() {
GuiContainerManager.addSlotClickHandler(instance);
GuiContainerManager.addInputHandler(instance);
if (CommonUtils.isClient()) registerClientCommands();
}
public static void load(GuiContainer gui) {
@@ -56,32 +67,48 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
return gui.field_147007_t && gui.field_147008_s.size() > 1;
}
@SideOnly(Side.CLIENT)
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) {
@@ -122,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);
@@ -134,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;
@@ -220,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);
@@ -1,13 +1,13 @@
package codechicken.nei;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
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;
@@ -17,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;
@@ -24,24 +25,30 @@ import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.LoadController;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModMetadata;
import cpw.mods.fml.common.discovery.ASMDataTable;
import cpw.mods.fml.common.event.FMLInitializationEvent;
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() {
@@ -49,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;
@@ -59,44 +66,31 @@ public class NEIModContainer extends DummyModContainer {
return gregTech5Loaded;
}
public static boolean isGTNHLibLoaded() {
return gtnhLibLoaded;
}
public static boolean isCCLoaded() {
return cubicChunksLoaded;
}
@Override
public Set<ArtifactVersion> getRequirements() {
Set<ArtifactVersion> deps = new HashSet<>();
deps.add(VersionParser.parseVersionReference("CodeChickenCore@[" + CodeChickenCorePlugin.version + ",)"));
deps.add(VersionParser.parseVersionReference("CodeChickenCore@[" + codechicken.core.asm.Tags.VERSION + ",)"));
return deps;
}
@Override
public List<ArtifactVersion> getDependencies() {
return new LinkedList<>(getRequirements());
List<ArtifactVersion> deps = new ArrayList<>();
deps.add(VersionParser.parseVersionReference("CodeChickenCore@[" + codechicken.core.asm.Tags.VERSION + ",)"));
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);
@@ -106,13 +100,19 @@ public class NEIModContainer extends DummyModContainer {
@Subscribe
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();
}
@@ -127,9 +127,17 @@ public class NEIModContainer extends DummyModContainer {
public void loadComplete(FMLLoadCompleteEvent event) {
if (CommonUtils.isClient()) {
GuiRecipeTab.loadHandlerInfo();
ClientHandler.loadPluginsList();
ClientHandler.loadHandlerOrdering();
asmDataTable = null;
}
}
@Subscribe
public void onServerAboutToStart(FMLServerAboutToStartEvent event) {
NEIServerConfig.resetFirstLoad();
}
@Subscribe
public void handleIMCMessages(FMLInterModComms.IMCEvent event) {
IMCHandler.processIMC(event.getMessages());
@@ -149,4 +157,8 @@ public class NEIModContainer extends DummyModContainer {
public Class<?> getCustomResourcePackClass() {
return getSource().isDirectory() ? FMLFolderResourcePack.class : FMLFileResourcePack.class;
}
public static ASMDataTable getAsmDataTable() {
return asmDataTable;
}
}
@@ -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;
}
+17
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;
@@ -88,9 +89,25 @@ public class NEISPH implements IServerPacketHandler {
case C2S.SET_DUMMY_SLOT:
handleDummySlotSet(sender, packet);
break;
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) {
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
NEIServerUtils.sendChatItemLink(sender, payloadTag);
}
private void handleDummySlotSet(EntityPlayerMP sender, PacketCustom packet) {
int slotNumber = packet.readShort();
ItemStack stack = packet.readItemStack(true);
@@ -18,9 +18,10 @@ 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;
import net.minecraftforge.permission.PermissionManager;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@@ -35,24 +36,38 @@ 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();
loadConfig();
loadBannedItems();
try {
Class.forName("net.minecraftforge.permission.PermissionManager");
doesPermAPIExist = true;
NEIActions.baseActions().forEach(
action -> {
PermissionManager.registerPermission("nei.permissions." + action, PermissionLevel.OP);
});
} catch (ClassNotFoundException ignored) {}
}
loadWorld(world);
}
@@ -93,6 +108,7 @@ public class NEIServerConfig {
setDefaultFeature("save-state");
setDefaultFeature("item");
setDefaultFeature("delete");
setDefaultFeature("autocraft", "ALL");
setDefaultFeature("notify-item", "CONSOLE, OP");
}
@@ -121,7 +137,12 @@ public class NEIServerConfig {
}
public static boolean canPlayerPerformAction(String playername, String name) {
return isPlayerInList(playername, getPlayerList("permissions." + NEIActions.base(name)), true);
String node = "permissions." + NEIActions.base(name);
boolean hasPerm = doesPermAPIExist
? PermissionManager.checkPermission(ServerUtils.getPlayer(playername), "nei." + node)
: false;
return hasPerm || isPlayerInList(playername, getPlayerList(node), true);
}
public static boolean isPlayerInList(String playername, Set<String> list, boolean allowCards) {
@@ -197,7 +218,7 @@ public class NEIServerConfig {
FileReader r = new FileReader(file);
int line = 0;
for (String s : IOUtils.readLines(r)) {
if (s.charAt(0) == '#' || s.trim().length() == 0) continue;
if (s.charAt(0) == '#' || s.trim().isEmpty()) continue;
int delim = s.lastIndexOf('=');
if (delim < 0) {
logger.error("line " + line + ": Missing =");
+132 -20
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;
@@ -17,6 +18,7 @@ import net.minecraft.enchantment.Enchantment;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.event.ClickEvent;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -27,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;
@@ -43,12 +46,30 @@ 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;
import cpw.mods.fml.relauncher.ReflectionHelper;
public class NEIServerUtils {
private static class GTMetaBaseItemHolder {
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;
}
}
}
public static boolean isRaining(World world) {
return world.getWorldInfo().isRaining();
}
@@ -56,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)
@@ -69,6 +90,48 @@ public class NEIServerUtils {
player.extinguish();
}
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) {
return world.getWorldInfo().getWorldTime();
}
@@ -89,9 +152,8 @@ public class NEIServerUtils {
else return player.inventory.getStackInSlot(slot);
}
@SuppressWarnings("unchecked")
public static void deleteAllItems(EntityPlayerMP player) {
for (Slot slot : (List<Slot>) player.openContainer.inventorySlots) slot.putStack(null);
for (Slot slot : player.openContainer.inventorySlots) slot.putStack(null);
player.sendContainerAndContentsToPlayer(player.openContainer, player.openContainer.getInventory());
}
@@ -147,7 +209,7 @@ public class NEIServerUtils {
/**
* NBT-friendly version of {@link #areStacksSameType(ItemStack, ItemStack)}
*
*
* @param stack1 The {@link ItemStack} being compared.
* @param stack2 The {@link ItemStack} to compare to.
* @return whether the two items are the same in terms of itemID, damage and NBT.
@@ -155,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());
}
@@ -178,19 +240,33 @@ public class NEIServerUtils {
/**
* NBT-friendly version of {@link #areStacksSameTypeCrafting(ItemStack, ItemStack)}
*
*
* @param stack1 The {@link ItemStack} being compared.
* @param stack2 The {@link ItemStack} to compare to.
* @return whether the two items are the same from the perspective of a crafting inventory.
*/
public static boolean areStacksSameTypeCraftingWithNBT(ItemStack stack1, ItemStack stack2) {
return stack1 != null && stack2 != null
&& stack1.getItem() == stack2.getItem()
&& (stack1.getItemDamage() == stack2.getItemDamage()
|| stack1.getItemDamage() == OreDictionary.WILDCARD_VALUE
|| stack2.getItemDamage() == OreDictionary.WILDCARD_VALUE
|| stack1.getItem().isDamageable())
&& NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound());
if (NEIServerUtils.areStacksSameTypeCrafting(stack2, stack1)) {
if (NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound())) return true;
if (isItemTool(stack1)
&& (stack1.stackTagCompound == null || stack1.stackTagCompound.hasKey("GT.ToolStats"))
&& (stack2.stackTagCompound == null || stack2.stackTagCompound.hasKey("GT.ToolStats")))
return true;
return stack1.getMaxStackSize() == 1 && stack2.getMaxStackSize() == 1
&& (stack1.stackTagCompound == null ^ stack2.stackTagCompound == null);
}
return false;
}
/**
* 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
*/
public static boolean isItemTool(ItemStack stack) {
return GTMetaBaseItemHolder.gtMetaBaseItem != null
&& GTMetaBaseItemHolder.gtMetaBaseItem.isInstance(stack.getItem());
}
/**
@@ -368,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) {
@@ -23,6 +23,8 @@ public final class PacketIDs {
public static final int SET_CREATIVE_PLUS_MODE = 23;
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 {
@@ -36,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;
}
}
+79 -125
View File
@@ -1,21 +1,22 @@
package codechicken.nei;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
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.ItemPanel.ItemPanelSlot;
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.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.recipe.GuiUsageRecipe;
public abstract class PanelWidget extends Widget {
public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ? extends ItemsGrid.MouseContext>>
extends Widget {
protected static final int PADDING = 2;
@@ -26,100 +27,80 @@ public abstract class PanelWidget extends Widget {
public Button pageNext;
public Label pageLabel;
protected ItemsGrid grid;
protected T grid;
public ArrayList<ItemStack> getItems() {
return grid.getItems();
return this.grid.getItems();
}
public ItemsGrid getGrid() {
return grid;
public T getGrid() {
return this.grid;
}
public void init() {
pageLabel = new Label("0/0", true);
pagePrev = new Button("Prev") {
pagePrev = new Button("<") {
public boolean onButtonPress(boolean rightclick) {
if (!rightclick) {
if (rightclick) {
grid.setPage(0);
} else {
grid.shiftPage(-1);
return true;
}
return false;
return true;
}
@Override
public String getRenderLabel() {
return "<";
public void draw(int mousex, int mousey) {
this.state = grid.getNumPages() <= 1 ? 2 : 0;
super.draw(mousex, mousey);
}
};
pageNext = new Button("Next") {
pageNext = new Button(">") {
public boolean onButtonPress(boolean rightclick) {
if (!rightclick) {
if (rightclick) {
grid.setPage(grid.getNumPages() - 1);
} else {
grid.shiftPage(1);
return true;
}
return false;
return true;
}
@Override
public String getRenderLabel() {
return ">";
public void draw(int mousex, int mousey) {
this.state = grid.getNumPages() <= 1 ? 2 : 0;
super.draw(mousex, mousey);
}
};
}
public abstract String getLabelText();
protected abstract String getPositioningSettingName();
public abstract Rectangle4i calculateBounds(GuiContainer gui);
public abstract int getMarginLeft(GuiContainer gui);
public abstract int getMarginTop(GuiContainer gui);
public abstract int getWidth(GuiContainer gui);
public abstract int getHeight(GuiContainer gui);
protected static int snapDownToMultiple(int value, int multiple) {
return (value / multiple) * multiple;
}
public void resize(GuiContainer gui) {
final Rectangle4i margin = new Rectangle4i(
getMarginLeft(gui),
getMarginTop(gui),
getWidth(gui),
getHeight(gui));
final Rectangle4i bounds = calculateBounds(gui);
final int minWidth = 5 * ItemsGrid.SLOT_SIZE;
final int minHeight = 9 * ItemsGrid.SLOT_SIZE;
final String settingName = getPositioningSettingName();
int paddingLeft = (int) Math
.ceil(margin.w * NEIClientConfig.getSetting(settingName + ".left").getIntValue() / 100000.0);
int paddingTop = (int) Math
.ceil(margin.h * NEIClientConfig.getSetting(settingName + ".top").getIntValue() / 100000.0);
int paddingRight = (int) Math
.ceil(margin.w * NEIClientConfig.getSetting(settingName + ".right").getIntValue() / 100000.0);
int paddingBottom = (int) Math
.ceil(margin.h * NEIClientConfig.getSetting(settingName + ".bottom").getIntValue() / 100000.0);
int deltaHeight = Math.min(0, margin.h - paddingTop - paddingBottom - minHeight) / 2;
paddingLeft = Math.min(paddingLeft, Math.max(0, margin.w - paddingRight - minWidth));
paddingRight = Math.min(paddingRight, Math.max(0, margin.w - paddingLeft - minWidth));
paddingTop = Math.min(margin.h - minHeight, Math.max(0, paddingTop + deltaHeight));
paddingBottom = Math.min(margin.h - paddingTop - minHeight, Math.max(0, paddingBottom - deltaHeight));
x = margin.x + paddingLeft;
y = margin.y + paddingTop;
w = margin.w - paddingLeft - paddingRight;
h = margin.h - paddingTop - paddingBottom;
x = bounds.x;
y = bounds.y;
w = bounds.w;
h = bounds.h;
final int header = resizeHeader(gui);
final int footer = resizeFooter(gui);
grid.setGridSize(x, y + header, w, h - header - footer);
grid.refresh(gui);
this.grid.setGridSize(x, y + header, w, h - header - footer);
this.grid.refresh(gui);
}
protected int resizeHeader(GuiContainer gui) {
@@ -164,22 +145,21 @@ public abstract class PanelWidget extends Widget {
protected abstract int resizeFooter(GuiContainer gui);
public void setVisible() {
if (grid.getPerPage() > 0) {
if (this.grid.getPerPage() > 0 && !this.grid.isEmpty()) {
LayoutManager.addWidget(pagePrev);
LayoutManager.addWidget(pageNext);
LayoutManager.addWidget(pageLabel);
grid.setVisible();
}
}
@Override
public void update() {
grid.update();
this.grid.update();
}
@Override
public void draw(int mousex, int mousey) {
grid.draw(mousex, mousey);
this.grid.draw(mousex, mousey);
}
@Override
@@ -191,60 +171,35 @@ public abstract class PanelWidget extends Widget {
}
}
@Override
public void postDrawTooltips(int mx, int my, List<String> tooltip) {
grid.postDrawTooltips(mx, my, tooltip);
}
@Override
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
if (this.grid.getSlotBySlotIndex(mouseDownSlot) == null) {
mouseDownSlot = -1;
}
if (mouseDownSlot >= 0 && draggedStack == null
&& NEIClientUtils.getHeldItem() == null
&& NEIClientConfig.hasSMPCounterPart()) {
ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
ItemsGridSlot mouseOverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500) {
draggedStack = getDraggedStackWithQuantity(mouseDownSlot);
draggedStack = getDraggedStackWithQuantity(this.grid.getSlotBySlotIndex(mouseDownSlot).getItemStack());
mouseDownSlot = -1;
}
}
}
protected abstract ItemStack getDraggedStackWithQuantity(int mouseDownSlot);
protected abstract ItemStack getDraggedStackWithQuantity(ItemStack itemStack);
@Override
public boolean handleClick(int mousex, int mousey, int button) {
if (handleClickExt(mousex, mousey, button)) return true;
if (NEIClientUtils.getHeldItem() != null) {
if (!grid.contains(mousex, mousey)) {
return false;
}
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")) {
if (button == 1) {
NEIClientUtils.decreaseSlotStack(-999);
} else {
NEIClientUtils.deleteHeldItem();
}
} else {
NEIClientUtils.dropHeldItem();
}
return true;
}
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (hoverSlot != null) {
if (button == 2) {
if (hoverSlot.item != null) {
draggedStack = getDraggedStackWithQuantity(hoverSlot.slotIndex);
}
draggedStack = getDraggedStackWithQuantity(hoverSlot.getItemStack());
} else {
mouseDownSlot = hoverSlot.slotIndex;
}
@@ -259,7 +214,7 @@ public abstract class PanelWidget extends Widget {
public boolean handleClickExt(int mouseX, int mouseY, int button) {
if (ItemPanels.itemPanel.draggedStack != null && ItemPanels.bookmarkPanel.contains(mouseX, mouseY)) {
ItemPanels.bookmarkPanel.addOrRemoveItem(ItemPanels.itemPanel.draggedStack, null, null, false, true);
ItemPanels.bookmarkPanel.addItem(ItemPanels.itemPanel.draggedStack);
ItemPanels.itemPanel.draggedStack = null;
return true;
}
@@ -325,24 +280,17 @@ public abstract class PanelWidget extends Widget {
@Override
public void mouseUp(int mousex, int mousey, int button) {
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
final ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
if (hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && draggedStack == null) {
ItemStack item = hoverSlot.item.copy();
if (NEIController.manager.window instanceof GuiRecipe || !NEIClientConfig.canCheatItem(item)) {
if (button == 0) {
GuiCraftingRecipe.openRecipeGui("item", item);
} else if (button == 1) {
GuiUsageRecipe.openRecipeGui("item", item);
}
mouseDownSlot = -1;
return;
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|| !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) {
ShortcutInputHandler.handleMouseClick(hoverSlot.getItemStack());
} else {
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1);
}
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.slotIndex), button, -1);
}
mouseDownSlot = -1;
@@ -352,20 +300,22 @@ public abstract class PanelWidget extends Widget {
public boolean onMouseWheel(int i, int mousex, int mousey) {
if (!contains(mousex, mousey)) return false;
grid.shiftPage(-i);
this.grid.shiftPage(-i);
return true;
}
@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")) {
grid.shiftPage(1);
if (KeyManager.isKeyDown("gui.next")) {
this.grid.shiftPage(1);
return true;
}
if (NEIClientConfig.isKeyHashDown("gui.prev")) {
grid.shiftPage(-1);
if (KeyManager.isKeyDown("gui.prev")) {
this.grid.shiftPage(-1);
return true;
}
@@ -374,24 +324,28 @@ public abstract class PanelWidget extends Widget {
@Override
public ItemStack getStackMouseOver(int mousex, int mousey) {
ItemPanelSlot slot = getSlotMouseOver(mousex, mousey);
return slot == null ? null : slot.item;
final ItemsGridSlot slot = this.grid.getSlotMouseOver(mousex, mousey);
return slot == null ? null : slot.getItemStack();
}
public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
return grid.getSlotMouseOver(mousex, mousey);
public ItemStack getStackMouseOverWithQuantity(int mousex, int mousey) {
final ItemStack hoverSlot = getStackMouseOver(mousex, mousey);
return hoverSlot != null ? getDraggedStackWithQuantity(hoverSlot) : null;
}
abstract ItemsGridSlot getSlotMouseOver(int mousex, int mousey);
public int getPage() {
return grid.getPage();
return this.grid.getPage();
}
public int getNumPages() {
return grid.getNumPages();
return this.grid.getNumPages();
}
@Override
public boolean contains(int px, int py) {
return grid.contains(px, py);
return this.grid.contains(px, py);
}
}
@@ -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;
}
@@ -1,25 +1,55 @@
package codechicken.nei;
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);
@@ -40,7 +70,7 @@ public class PositionedStack {
public void generatePermutations() {
if (permutated) return;
ArrayList<ItemStack> stacks = new ArrayList<>();
List<ItemStack> stacks = new ArrayList<>();
for (ItemStack item : items) {
if (item == null || item.getItem() == null) continue;
@@ -74,19 +104,117 @@ 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() {
return new PositionedStack(items, relx, rely);
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() {
return getFilteredPermutations(null);
}
public List<ItemStack> getFilteredPermutations(ItemFilter additionalFilter) {
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::containsManual).reversed());
return items;
}
private List<ItemStack> filteringPermutations(List<ItemStack> items, ItemFilter filter) {
if (filter == null) return items;
final List<ItemStack> filteredItems = items.stream().filter(filter::matches).collect(Collectors.toList());
return filteredItems.isEmpty() ? items : filteredItems;
}
public int getPermutationIndex(ItemStack stack) {
for (int index = 0; index < this.items.length; index++) {
if (NEIServerUtils.areStacksSameType(items[index], stack)) {
return index;
}
}
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) {
item = items[index].copy();
this.item = this.items[index].copy();
if (item.getItem() == null) {
item = new ItemStack(Blocks.fire);
} else if (item.getItemDamage() == OreDictionary.WILDCARD_VALUE && item.getItem() != null
&& item.getItem().isRepairable()) {
item.setItemDamage(0);
}
if (this.item.getItem() == null) {
this.item = new ItemStack(Blocks.fire);
} else if (this.item.getItemDamage() == OreDictionary.WILDCARD_VALUE && this.item.getItem().isRepairable()) {
this.item.setItemDamage(0);
}
}
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) {
@@ -99,7 +227,7 @@ public class PositionedStack {
* NBT-friendly version of {@link #contains(ItemStack)}
*/
public boolean containsWithNBT(ItemStack ingredient) {
for (ItemStack item : items) if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(item, ingredient)) return true;
for (ItemStack item : items) if (StackInfo.equalItemAndNBT(item, ingredient, true)) return true;
return false;
}
@@ -114,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);
}
}
}
}
@@ -0,0 +1,269 @@
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.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import net.minecraft.item.ItemStack;
import org.apache.commons.io.IOUtils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import codechicken.core.CommonUtils;
import codechicken.nei.SubsetWidget.SubsetTag;
import codechicken.nei.api.API;
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.recipe.IRecipeHandler;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
public class PresetsList {
public enum PresetMode {
SUBSET,
HIDE,
REMOVE,
GROUP;
}
public static class Preset implements ItemFilter {
public String name = "";
public boolean enabled = true;
public PresetMode mode = PresetMode.HIDE;
public Set<String> items = new HashSet<>();
public static String getIdentifier(ItemStack stack) {
return StackInfo.getItemStackGUID(stack);
}
@Override
public boolean matches(ItemStack item) {
return items.contains(Preset.getIdentifier(item));
}
public Preset copy() {
Preset preset = new Preset();
preset.name = name;
preset.enabled = enabled;
preset.mode = mode;
preset.items = new HashSet<>(items);
return preset;
}
}
protected static class PresetRecipeFilter implements IRecipeFilterProvider, IRecipeFilter {
public Set<String> cache;
public IRecipeFilter getRecipeFilter() {
if (cache == null) {
cache = PresetsList.presets.stream().filter(p -> p.enabled && p.mode == PresetMode.REMOVE)
.flatMap(p -> p.items.stream()).collect(Collectors.toSet());
}
return cache.isEmpty() ? null : this;
}
@Override
public boolean matches(IRecipeHandler handler, int recipeIndex) {
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;
}
return result == null && others.isEmpty();
}
private boolean matchPositionedStack(List<PositionedStack> items, boolean dir) {
for (PositionedStack pStack : items) {
if (matchPositionedStack(pStack) == dir) {
return true;
}
}
return false;
}
private boolean matchPositionedStack(PositionedStack pStack) {
for (ItemStack stack : pStack.items) {
if (!cache.contains(Preset.getIdentifier(stack))) {
return true;
}
}
return false;
}
}
protected static class ItemPanelFilter implements ItemFilterProvider, ItemFilter {
public Set<String> cache;
public ItemFilter getFilter() {
if (cache == null) {
cache = PresetsList.presets.stream()
.filter(p -> p.enabled && (p.mode == PresetMode.HIDE || p.mode == PresetMode.REMOVE))
.flatMap(p -> p.items.stream()).collect(Collectors.toSet());
}
return cache.isEmpty() ? null : this;
}
@Override
public boolean matches(ItemStack stack) {
return !cache.contains(Preset.getIdentifier(stack));
}
}
public static final List<Preset> presets = new ArrayList<>();
protected static PresetRecipeFilter recipeFilter = new PresetRecipeFilter();
protected static ItemPanelFilter itemFilter = new ItemPanelFilter();
static {
API.addItemFilter(itemFilter);
API.addRecipeFilter(recipeFilter);
}
public static ItemFilter getItemFilter() {
return itemFilter.getFilter();
}
public static void load() {
final File presetsFile = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/presets.ini");
if (!presetsFile.exists()) return;
List<String> itemStrings;
try (FileInputStream reader = new FileInputStream(presetsFile)) {
NEIClientConfig.logger.info("Loading presets from file {}", presetsFile);
itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to load presets from file {}", presetsFile, e);
return;
}
JsonParser parser = new JsonParser();
Preset preset = new Preset();
presets.clear();
for (String itemStr : itemStrings) {
try {
if (itemStr.isEmpty()) {
itemStr = "; {}";
}
if (itemStr.startsWith("; ")) {
JsonObject settings = parser.parse(itemStr.substring(2)).getAsJsonObject();
if (!preset.items.isEmpty()) {
// do not create empty namespaces
presets.add(preset);
preset = new Preset();
}
if (settings.has("name")) {
preset.name = settings.get("name").getAsString();
}
if (settings.has("enabled")) {
preset.enabled = settings.get("enabled").getAsBoolean();
}
if (settings.has("mode")) {
preset.mode = PresetMode.valueOf(settings.get("mode").getAsString());
}
} else {
preset.items.add(itemStr);
}
} catch (IllegalArgumentException | JsonSyntaxException | IllegalStateException e) {
NEIClientConfig.logger.error("Failed to load presets ItemStack from json string:\n{}", itemStr);
}
}
if (!preset.items.isEmpty()) {
presets.add(preset);
}
updateSubsets();
}
public static void savePresets() {
final File presetsFile = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/presets.ini");
final List<String> strings = new ArrayList<>();
for (Preset preset : presets) {
JsonObject settings = new JsonObject();
settings.add("name", new JsonPrimitive(preset.name));
settings.add("enabled", new JsonPrimitive(preset.enabled));
settings.add("mode", new JsonPrimitive(preset.mode.toString()));
strings.add("; " + NBTJson.toJson(settings));
strings.addAll(preset.items);
}
try (FileOutputStream output = new FileOutputStream(presetsFile)) {
IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8);
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to save presets list to file {}", presetsFile, e);
}
recipeFilter.cache = null;
itemFilter.cache = null;
updateSubsets();
CollapsibleItems.saveStates();
CollapsibleItems.load();
ItemList.refreshItems.restart();
}
private static void updateSubsets() {
SubsetWidget.removeTag("Presets");
for (Preset preset : PresetsList.presets) {
if (preset.enabled && preset.mode == PresetMode.SUBSET) {
SubsetWidget.addTag(
new SubsetTag(
"Presets." + preset.name,
stack -> preset.items.contains(Preset.getIdentifier(stack))));
}
}
}
}
File diff suppressed because it is too large. Load diff
@@ -2,10 +2,14 @@ package codechicken.nei;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.SearchField.GuiSearchField;
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.util.TextHistory;
public abstract class RecipeSearchField extends TextField {
public abstract class RecipeSearchField extends TextField implements ItemFilterProvider, IRecipeFilterProvider {
private static final TextHistory history = new TextHistory();
@@ -14,6 +18,16 @@ public abstract class RecipeSearchField extends TextField {
this.field.setVisible(false);
}
@Override
protected void initInternalTextField() {
final GuiSearchField field = new GuiSearchField();
field.setMaxStringLength(maxSearchLength);
field.setCursorPositionZero();
field.setSkipRecipeTokens(true);
((SearchTextFormatter) field.getFormatter()).setContextToken("<>");
this.field = field;
}
public boolean isVisible() {
return this.field.getVisible();
}
@@ -34,15 +48,19 @@ public abstract class RecipeSearchField extends TextField {
}
@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
@@ -50,8 +68,14 @@ public abstract class RecipeSearchField extends TextField {
return EnumChatFormatting.getTextWithoutFormattingCodes(s);
}
@Override
public ItemFilter getFilter() {
return SearchField.getFilter(text());
return ((GuiSearchField) field).getFilter();
}
@Override
public IRecipeFilter getRecipeFilter() {
return ((GuiSearchField) field).getRecipeFilter();
}
@Override
+245 -62
View File
@@ -1,21 +1,25 @@
package codechicken.nei;
import static codechicken.nei.NEIClientConfig.world;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import net.minecraftforge.fluids.FluidStack;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.ItemList.AnyMultiItemFilter;
import codechicken.nei.ItemList.EverythingItemFilter;
import codechicken.nei.ItemList.PatternItemFilter;
import codechicken.nei.api.API;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
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.recipe.StackInfo;
import codechicken.nei.util.TextHistory;
public class SearchField extends TextField implements ItemFilterProvider {
@@ -23,6 +27,7 @@ public class SearchField extends TextField implements ItemFilterProvider {
/**
* Interface for returning a custom filter based on search field text
*/
@Deprecated
public static interface ISearchProvider {
/**
@@ -36,34 +41,115 @@ public class SearchField extends TextField implements ItemFilterProvider {
public ItemFilter getFilter(String searchText);
}
private static class DefaultSearchProvider implements ISearchProvider {
public static class SearchParserProvider implements ISearchParserProvider {
@Override
public boolean isPrimary() {
return false;
protected final Function<Pattern, ItemFilter> createFilter;
protected final String name;
protected final char prefix;
protected final EnumChatFormatting highlightedColor;
public SearchParserProvider(char prefix, String name, EnumChatFormatting highlightedColor,
Function<Pattern, ItemFilter> createFilter) {
this.prefix = prefix;
this.name = name;
this.highlightedColor = highlightedColor;
this.createFilter = createFilter;
}
@Override
public ItemFilter getFilter(String searchText) {
Pattern pattern = getPattern(searchText);
return pattern == null ? null : new PatternItemFilter(pattern);
Pattern pattern = SearchField.getPattern(searchText);
if (pattern != null) {
return this.createFilter(pattern);
}
return new NothingItemFilter();
}
protected ItemFilter createFilter(Pattern pattern) {
final ItemFilter filter = this.createFilter.apply(pattern);
return filter == null ? new NothingItemFilter() : filter;
}
@Override
public char getPrefix() {
return this.prefix;
}
@Override
public EnumChatFormatting getHighlightedColor() {
return this.highlightedColor;
}
@Override
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);
}
public GuiSearchField(SearchTokenParser searchParser) {
super(Minecraft.getMinecraft().fontRenderer, 0, 0, 0, 0);
this.searchParser = searchParser;
setFormatter(new SearchTextFormatter(searchParser));
setDisabledTextColour(0xFF303030);
setPlaceholder(NEIClientUtils.translate("inventory.search.placeholder"));
}
public void setSkipRecipeTokens(boolean skipRecipeTokens) {
this.skipRecipeTokens = skipRecipeTokens;
}
@Override
public ItemFilter getFilter() {
return getFilter(getText());
}
@Override
public IRecipeFilter getRecipeFilter() {
return getRecipeFilter(getText());
}
public ItemFilter getFilter(String filterText) {
return this.searchParser.getFilter(filterText, this.skipRecipeTokens);
}
public IRecipeFilter getRecipeFilter(String filterText) {
return this.searchParser.getRecipeFilter(filterText);
}
}
@Deprecated
public static List<ISearchProvider> searchProviders = new LinkedList<>();
public static final SearchTokenParser searchParser = new SearchTokenParser();
private static final TextHistory history = new TextHistory();
private boolean isVisible = true;
long lastclicktime;
private long lastclicktime;
public SearchField(String ident) {
super(ident);
API.addItemFilter(this);
API.addSearchProvider(new DefaultSearchProvider());
}
@Override
protected void initInternalTextField() {
field = new GuiSearchField();
field.setMaxStringLength(maxSearchLength);
field.setCursorPositionZero();
}
public static boolean searchInventories() {
return world.nbt.getBoolean("searchinventories");
return NEIClientConfig.world.nbt.getBoolean("searchinventories");
}
public boolean isVisible() {
@@ -76,7 +162,8 @@ public class SearchField extends TextField implements ItemFilterProvider {
@Override
public int getTextColour() {
if (ItemPanels.itemPanel.getItems().isEmpty()) {
if (ItemPanels.itemPanel.getItems().isEmpty()
&& (!this.field.getText().isEmpty() || !NEIClientConfig.hideItemsUntilSearching())) {
return focused() ? 0xFFcc3300 : 0xFF993300;
} else {
return focused() ? 0xFFE0E0E0 : 0xFF909090;
@@ -85,14 +172,37 @@ public class SearchField extends TextField implements ItemFilterProvider {
@Override
public void draw(int mousex, int mousey) {
super.draw(mousex, mousey);
if (searchInventories()) {
GuiDraw.drawGradientRect(x - 1, y - 1, 1, h + 2, 0xFFFFFF00, 0xFFC0B000); // Left
GuiDraw.drawGradientRect(x - 1, y - 1, w + 2, 1, 0xFFFFFF00, 0xFFC0B000); // Top
GuiDraw.drawGradientRect(x + w, y - 1, 1, h + 2, 0xFFFFFF00, 0xFFC0B000); // Left
GuiDraw.drawGradientRect(x - 1, y + h, w + 2, 1, 0xFFFFFF00, 0xFFC0B000); // Bottom
GuiDraw.drawGradientRect(
field.xPosition - 1,
field.yPosition - 1,
1,
field.height + 2,
0xFFFFFF00,
0xFFC0B000); // Left
GuiDraw.drawGradientRect(
field.xPosition - 1,
field.yPosition - 1,
field.width + 2,
1,
0xFFFFFF00,
0xFFC0B000); // Top
GuiDraw.drawGradientRect(
field.xPosition + field.width,
field.yPosition - 1,
1,
field.height + 2,
0xFFFFFF00,
0xFFC0B000); // Left
GuiDraw.drawGradientRect(
field.xPosition - 1,
field.yPosition + field.height,
field.width + 2,
1,
0xFFFFFF00,
0xFFC0B000); // Bottom
}
}
@@ -110,25 +220,31 @@ public class SearchField extends TextField implements ItemFilterProvider {
@Override
public void onTextChange(String oldText) {
final String newText = text();
if (newText.length() > 0) NEIClientConfig.logger.debug("Searching for " + text());
NEIClientConfig.setSearchExpression(newText);
ItemList.updateFilter.restart();
if (!newText.equals(oldText)) {
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
@@ -137,47 +253,114 @@ public class SearchField extends TextField implements ItemFilterProvider {
}
public static Pattern getPattern(String search) {
switch (NEIClientConfig.getIntSetting("inventory.searchmode")) {
return getPattern(search, NEIClientConfig.getIntSetting("inventory.search.patternMode"));
}
public static Pattern getPattern(String search, int patternMode) {
switch (patternMode) {
case 0: // plain
search = "\\Q" + search + "\\E";
search = Pattern.quote(search);
break;
case 1: // extended
if (search.length() >= 3 && search.startsWith("r/") && search.endsWith("/")) {
search = search.substring(2, search.length() - 1);
} else {
final Matcher matcher = Pattern.compile("(\\?|\\*)").matcher(search);
final StringBuilder cleanedString = new StringBuilder();
int lastEndIndex = 0;
while (matcher.find()) {
cleanedString.append(Pattern.quote(search.substring(lastEndIndex, matcher.start())));
switch (matcher.group(0).charAt(0)) {
case '?':
cleanedString.append(".");
break;
case '*':
cleanedString.append(".+?");
break;
default:
break;
}
lastEndIndex = matcher.end();
}
search = cleanedString + Pattern.quote(search.substring(lastEndIndex, search.length()));
}
break;
default:
break;
}
// regex and extended+
if (!search.isEmpty()) {
try {
return Pattern.compile(search, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
} catch (PatternSyntaxException ignored) {}
}
return null;
}
public static String getEscapedSearchText(ItemStack stack) {
final FluidStack fluidStack = StackInfo.getFluid(stack);
String displayName;
if (fluidStack != null) {
displayName = fluidStack.getLocalizedName();
} else {
displayName = stack.getDisplayName();
}
return getEscapedSearchText(displayName);
}
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:
search = search.replace(".", "").replace("?", ".").replace("*", ".+?");
text = text.replaceAll("[\\?|\\*]", "\\\\$0");
break;
case 2:
text = text.replaceAll("[{}()\\[\\].+*?^$\\\\|]", "\\\\$0");
break;
case 3:
// 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;
}
Pattern pattern = null;
try {
pattern = Pattern.compile(search);
} catch (PatternSyntaxException ignored) {}
if (addQuotes) {
text = "\"" + text + "\"";
}
return pattern == null || pattern.toString().length() == 0 ? null : pattern;
return text;
}
@Override
public ItemFilter getFilter() {
return getFilter(text());
if (field.getText().isEmpty() && NEIClientConfig.hideItemsUntilSearching()) {
return new NothingItemFilter();
}
return ((GuiSearchField) field).getFilter();
}
public static ItemFilter getFilter(String s_filter) {
List<ItemFilter> primary = new LinkedList<>();
List<ItemFilter> secondary = new LinkedList<>();
s_filter = s_filter.toLowerCase();
for (ISearchProvider p : searchProviders) {
ItemFilter filter = p.getFilter(s_filter);
if (filter != null) (p.isPrimary() ? primary : secondary).add(filter);
}
if (!primary.isEmpty()) {
return new AnyMultiItemFilter(primary);
}
if (!secondary.isEmpty()) {
return new AnyMultiItemFilter(secondary);
}
return new EverythingItemFilter();
public static ItemFilter getFilter(String filterText) {
return SearchField.searchParser.getFilter(filterText);
}
@Override
@@ -0,0 +1,96 @@
package codechicken.nei;
import net.minecraft.util.EnumChatFormatting;
import codechicken.nei.FormattedTextField.TextFormatter;
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) {
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
if (spaceModeEnabled == 1) {
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
}
final SearchExpressionFormatVisitor visitor = new SearchExpressionFormatVisitor(searchParser);
return SearchExpressionUtils.visitRecipeSearchExpression(text, visitor);
} else {
final StringBuilder formattedText = new StringBuilder();
for (String[] orQuery : searchParser.splitByDelimiters(text, "|", false)) {
final String filterText = orQuery[1];
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.OR + orQuery[0]);
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 (contextPart[1].isEmpty()) {
continue;
}
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;
}
formattedText.append(contextPart[1].substring(startIndex, contextPart[1].length()));
}
}
return formattedText.toString();
}
}
}
@@ -0,0 +1,497 @@
package codechicken.nei;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
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.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 {
public enum SearchMode {
ALWAYS,
PREFIX,
NEVER;
public static SearchMode fromInt(int value) {
return switch (value) {
case 0 -> ALWAYS;
case 1 -> PREFIX;
default -> NEVER;
};
}
}
public static interface ISearchParserProvider {
public ItemFilter getFilter(String searchText);
public static List<Language> getAllLanguages() {
return new ArrayList<>(Minecraft.getMinecraft().getLanguageManager().getLanguages());
}
default List<Language> getMatchingLanguages() {
return getAllLanguages();
}
public char getPrefix();
public EnumChatFormatting getHighlightedColor();
default SearchMode getSearchMode() {
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search." + getName() + "SearchMode"));
}
default String getName() {
return String.valueOf(getPrefix());
}
}
private static class ProvidersCache {
public String languageCode = null;
public List<ISearchParserProvider> providers = new ArrayList<>();
public void clear() {
this.languageCode = null;
this.providers.clear();
}
}
public static class IsRegisteredItemFilter implements ItemFilter {
public ItemFilter filter;
public IsRegisteredItemFilter(ItemFilter filter) {
this.filter = filter;
}
@Override
public boolean matches(ItemStack item) {
return item != null && item.getItem() != null
&& item.getItem().delegate.name() != null
&& this.filter.matches(item);
}
}
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 ProvidersCache providersCache = new ProvidersCache();
protected final Map<Character, Character> prefixRedefinitions = new HashMap<>();
protected String prefixes = null;
public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
this.searchProviders = searchProviders;
}
public SearchTokenParser() {
this(new ArrayList<>());
}
public void addProvider(ISearchParserProvider provider) {
this.searchProviders.add(provider);
clearCache();
}
public synchronized void clearCache() {
this.providersCache.clear();
this.filtersCache.clear();
this.recipesCache.clear();
this.prefixes = null;
}
public List<ISearchParserProvider> getProviders() {
Language currentLanguage = Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage();
if (!currentLanguage.getLanguageCode().equals(providersCache.languageCode)) {
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);
if (provider.getMatchingLanguages().contains(currentLanguage)) {
if (provider.getSearchMode() == SearchMode.PREFIX && !providers.containsKey(provider.getPrefix())) {
providers.put(provider.getPrefix(), provider);
} else if (provider.getSearchMode() == SearchMode.ALWAYS) {
providersList.add(provider);
}
}
}
providersList.addAll(providers.values());
providersCache.languageCode = currentLanguage.getLanguageCode();
providersCache.providers = providersList;
}
return providersCache.providers;
}
public boolean hasRedefinedPrefix(char ch) {
return getPrefixes().indexOf(ch) >= 0;
}
public ISearchParserProvider getProvider(char ch) {
if (!hasRedefinedPrefix(ch)) {
return null;
}
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchMode.PREFIX && getRedefinedPrefix(provider.getPrefix()) == ch) {
return provider;
}
}
return null;
}
public List<ItemFilter> getAlwaysProvidersFilters(String searchText) {
final List<ItemFilter> filters = new ArrayList<>();
for (ISearchParserProvider provider : getProviders()) {
if (provider.getSearchMode() == SearchMode.ALWAYS) {
filters.add(provider.getFilter(searchText));
}
}
return filters;
}
public synchronized ItemFilter getFilter(String filterText) {
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 patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
if (patternMode != 3) {
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();
} else if (searchTokens.size() == 1) {
return new IsRegisteredItemFilter(searchTokens.get(0));
} else {
return new IsRegisteredItemFilter(new AnyMultiItemFilter(searchTokens));
}
});
} else {
return this.filtersCache.computeIfAbsent(filterText, text -> {
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
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) {
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
if (filterText.isEmpty()) {
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
}
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 int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
final String prefixes = getPrefixes();
SearchToken lastToken = null;
int lastEnd = 0;
for (String[] part : splitByDelimiters(filterText, "-!" + prefixes, true)) {
final SearchToken token = createToken(lastEnd, part, prefixes);
final int length = part[0].length() + part[1].length();
if (token.isSimpleFilter() && token.rawText.isEmpty()) {
lastEnd += length;
continue;
}
if (spaceMode == 0) { // and
tokens.add(lastToken = token);
} else if (spaceMode == 1) { // space
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);
}
}
lastEnd += length;
}
return tokens;
}
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);
}
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);
}
}
@@ -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;
}
}
@@ -9,7 +9,6 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.boss.BossStatus;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.minecraftforge.client.IItemRenderer;
import org.lwjgl.opengl.GL11;
@@ -27,21 +26,16 @@ public class SpawnerRenderer implements IItemRenderer {
public void renderInventoryItem(RenderBlocks render, ItemStack item) {
int meta = item.getItemDamage();
if (meta == 0) meta = ItemMobSpawner.idPig;
String bossName = BossStatus.bossName;
int bossTimeout = BossStatus.statusBarTime;
boolean bossHasColorModifier = BossStatus.hasColorModifier;
Entity entity = ItemMobSpawner.getEntity(meta);
try {
World world = NEIClientUtils.mc().theWorld;
ItemMobSpawner.loadSpawners(world);
CCRenderState.changeTexture(TextureMap.locationBlocksTexture);
render.renderBlockAsItem(Blocks.mob_spawner, 0, 1F);
entity.setWorld(render.minecraftRB.theWorld);
GL11.glPushMatrix();
Entity entity = ItemMobSpawner.getEntity(meta);
entity.setWorld(world);
float f1 = 0.4375F;
if (entity.getShadowSize() > 1.5) f1 = 0.1F;
GL11.glRotatef((float) (ClientUtils.getRenderTime() * 10), 0.0F, 1.0F, 0.0F);
@@ -51,7 +45,6 @@ public class SpawnerRenderer implements IItemRenderer {
entity.setLocationAndAngles(0, 0, 0, 0.0F, 0.0F);
RenderManager.instance.renderEntityWithPosYaw(entity, 0.0D, 0.0D, 0.0D, 0.0F, 0);
GL11.glPopMatrix();
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GL11.glDisable(GL11.GL_TEXTURE_2D);
@@ -64,7 +57,6 @@ public class SpawnerRenderer implements IItemRenderer {
BossStatus.hasColorModifier = bossHasColorModifier;
}
@SuppressWarnings("incomplete-switch")
@Override
public void renderItem(ItemRenderType type, ItemStack item, Object... data) {
switch (type) {
@@ -75,6 +67,8 @@ public class SpawnerRenderer implements IItemRenderer {
case ENTITY:
renderInventoryItem((RenderBlocks) data[0], item);
break;
default:
break;
}
}
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -9,7 +9,7 @@ public abstract class TextField extends Widget {
protected GuiTextField field;
private static final int maxSearchLength = 256;
protected static final int maxSearchLength = 256;
public boolean centered;
public String identifier;
@@ -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);
@@ -6,9 +6,9 @@ import cpw.mods.fml.common.FMLCommonHandler;
public class ThreadOperationTimer extends Thread {
@SuppressWarnings("serial")
public static class TimeoutException extends RuntimeException {
private static final long serialVersionUID = -8621458005263888185L;
public final Object operation;
public TimeoutException(String msg, Object op) {
@@ -15,10 +15,6 @@ public class VisiblityData {
* Subset dropdown
*/
public boolean showSubsetDropdown = true;
/**
* Presets dropdown
*/
public boolean showPresetsDropdown = true;
/**
* Item and search section
*/
@@ -44,7 +40,6 @@ public class VisiblityData {
public void translateDependancies() {
if (!showNEI) showWidgets = false;
if (!showWidgets) showItemSection = showUtilityButtons = false;
if (!showItemSection)
showBookmarkPanel = showSubsetDropdown = showPresetsDropdown = showSearchSection = showItemPanel = false;
if (!showItemSection) showBookmarkPanel = showSubsetDropdown = showSearchSection = showItemPanel = false;
}
}
+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;
}
}
@@ -11,6 +11,6 @@ public class WidgetZOrder implements Comparator<Widget> {
}
public int compare(Widget w1, Widget w2) {
return w1.z != w2.z ? ((topfirst ? w1.z > w2.z : w1.z < w2.z) ? 1 : -1) : 1;
return w1.z != w2.z ? ((topfirst ? w1.z < w2.z : w1.z > w2.z) ? 1 : -1) : 1;
}
}
@@ -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,8 +21,11 @@ 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;
public class WorldOverlayRenderer implements IKeyStateTracker {
@@ -25,21 +34,23 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
public static int mobOverlay = 0;
private static byte[] mobSpawnCache;
private static long mobOverlayUpdateTime;
private static String oregenPatternName;
private static boolean hasOregenPatternBeenSet = false;
public static void reset() {
mobOverlay = 0;
chunkOverlay = 0;
hasOregenPatternBeenSet = false;
}
@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,12 +237,17 @@ 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);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glLineWidth(1.5F);
GL11.glBegin(GL11.GL_LINES);
final Tessellator tess = Tessellator.instance;
tess.startDrawing(GL11.GL_LINES);
for (int cx = -4; cx <= 4; cx++) for (int cz = -4; cz <= 4; cz++) {
double x1 = ((entity.chunkCoordX + cx) << 4) - intOffsetX;
@@ -215,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;
@@ -230,22 +272,38 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
double dist = Math.pow(1.5, -(cx * cx + cz * cz));
GL11.glColor4d(0.9, 0, 0, dist);
tess.setColorRGBA_F(0.9F, 0, 0, (float) dist);
if (cx >= 0 && cz >= 0) {
GL11.glVertex3d(x2, y1, z2);
GL11.glVertex3d(x2, y2, z2);
tess.addVertex(x2, y1, z2);
tess.addVertex(x2, y2, z2);
}
if (cx >= 0 && cz <= 0) {
GL11.glVertex3d(x2, y1, z1);
GL11.glVertex3d(x2, y2, z1);
tess.addVertex(x2, y1, z1);
tess.addVertex(x2, y2, z1);
}
if (cx <= 0 && cz >= 0) {
GL11.glVertex3d(x1, y1, z2);
GL11.glVertex3d(x1, y2, z2);
tess.addVertex(x1, y1, z2);
tess.addVertex(x1, y2, z2);
}
if (cx <= 0 && cz <= 0) {
GL11.glVertex3d(x1, y1, z1);
GL11.glVertex3d(x1, y2, z1);
tess.addVertex(x1, y1, z1);
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) {
@@ -253,53 +311,49 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
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;
GL11.glColor4d(0, 0.9, 0, 0.4);
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++) {
GL11.glVertex3d(x2, y, z1);
GL11.glVertex3d(x2, y, z2);
GL11.glVertex3d(x1, y, z1);
GL11.glVertex3d(x1, y, z2);
GL11.glVertex3d(x1, y, z2);
GL11.glVertex3d(x2, y, z2);
GL11.glVertex3d(x1, y, z1);
GL11.glVertex3d(x2, y, z1);
tess.addVertex(x2, y, z1);
tess.addVertex(x2, y, z2);
tess.addVertex(x1, y, z1);
tess.addVertex(x1, y, z2);
tess.addVertex(x1, y, z2);
tess.addVertex(x2, y, z2);
tess.addVertex(x1, y, z1);
tess.addVertex(x2, y, z1);
}
for (double h = 1; h <= 15; h++) {
GL11.glVertex3d(x1 + h, y1, z1);
GL11.glVertex3d(x1 + h, y2, z1);
GL11.glVertex3d(x1 + h, y1, z2);
GL11.glVertex3d(x1 + h, y2, z2);
GL11.glVertex3d(x1, y1, z1 + h);
GL11.glVertex3d(x1, y2, z1 + h);
GL11.glVertex3d(x2, y1, z1 + h);
GL11.glVertex3d(x2, y2, z1 + h);
tess.addVertex(x1 + h, y1, z1);
tess.addVertex(x1 + h, y2, z1);
tess.addVertex(x1 + h, y1, z2);
tess.addVertex(x1 + h, y2, z2);
tess.addVertex(x1, y1, z1 + h);
tess.addVertex(x1, y2, z1 + h);
tess.addVertex(x2, y1, z1 + h);
tess.addVertex(x2, y2, z1 + h);
}
} else if (chunkOverlay == 3) {
if (!hasOregenPatternBeenSet) {
try {
oregenPatternName = ((Enum<?>) Class.forName("gregtech.common.GT_Worldgenerator")
.getDeclaredField("oregenPattern").get(null)).name();
} catch (Exception ignored) {
oregenPatternName = "AXISSYMMETRICAL";
}
hasOregenPatternBeenSet = true;
}
int gx1;
int gz1;
if (oregenPatternName.equals("EQUAL_SPACING")) {
if (getOregenPatternName().equals("EQUAL_SPACING")) {
// gt oregen uses new regular pattern
gx1 = ((Math.floorDiv(entity.chunkCoordX, 3) * 3) << 4) - intOffsetX;
gz1 = ((Math.floorDiv(entity.chunkCoordZ, 3) * 3) << 4) - intOffsetZ;
@@ -319,55 +373,106 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
int gx2 = gx1 + 48;
int gz2 = gz1 + 48;
GL11.glColor4d(0, 0.9, 0, 0.4);
tess.setColorRGBA_F(0, 0.9F, 0, 0.4F);
for (double y = (int) y1; y <= y2; y++) {
GL11.glVertex3d(gx2, y, gz1);
GL11.glVertex3d(gx2, y, gz2);
GL11.glVertex3d(gx1, y, gz1);
GL11.glVertex3d(gx1, y, gz2);
GL11.glVertex3d(gx1, y, gz2);
GL11.glVertex3d(gx2, y, gz2);
GL11.glVertex3d(gx1, y, gz1);
GL11.glVertex3d(gx2, y, gz1);
tess.addVertex(gx2, y, gz1);
tess.addVertex(gx2, y, gz2);
tess.addVertex(gx1, y, gz1);
tess.addVertex(gx1, y, gz2);
tess.addVertex(gx1, y, gz2);
tess.addVertex(gx2, y, gz2);
tess.addVertex(gx1, y, gz1);
tess.addVertex(gx2, y, gz1);
}
for (double h = 4; h <= 44; h += 4) {
if (h % 16 == 0) {
continue;
}
GL11.glVertex3d(gx1 + h, y1, gz1);
GL11.glVertex3d(gx1 + h, y2, gz1);
GL11.glVertex3d(gx1 + h, y1, gz2);
GL11.glVertex3d(gx1 + h, y2, gz2);
GL11.glVertex3d(gx1, y1, gz1 + h);
GL11.glVertex3d(gx1, y2, gz1 + h);
GL11.glVertex3d(gx2, y1, gz1 + h);
GL11.glVertex3d(gx2, y2, gz1 + h);
tess.addVertex(gx1 + h, y1, gz1);
tess.addVertex(gx1 + h, y2, gz1);
tess.addVertex(gx1 + h, y1, gz2);
tess.addVertex(gx1 + h, y2, gz2);
tess.addVertex(gx1, y1, gz1 + h);
tess.addVertex(gx1, y2, gz1 + h);
tess.addVertex(gx2, y1, gz1 + h);
tess.addVertex(gx2, y2, gz1 + h);
}
GL11.glColor4d(0, 0, 0.9, 0.4);
tess.setColorRGBA_F(0, 0, 0.9F, 0.4F);
gx1 += 23;
gz1 += 23;
gx2 = gx1 + 1;
gz2 = gz1 + 1;
GL11.glVertex3d(gx1, y1, gz1);
GL11.glVertex3d(gx1, y2, gz1);
GL11.glVertex3d(gx2, y1, gz1);
GL11.glVertex3d(gx2, y2, gz1);
GL11.glVertex3d(gx1, y1, gz2);
GL11.glVertex3d(gx1, y2, gz2);
GL11.glVertex3d(gx2, y1, gz2);
GL11.glVertex3d(gx2, y2, gz2);
tess.addVertex(gx1, y1, gz1);
tess.addVertex(gx1, y2, gz1);
tess.addVertex(gx2, y1, gz1);
tess.addVertex(gx2, y2, gz1);
tess.addVertex(gx1, y1, gz2);
tess.addVertex(gx1, y2, gz2);
tess.addVertex(gx2, y1, gz2);
tess.addVertex(gx2, y2, gz2);
}
}
}
GL11.glEnd();
tess.draw();
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glDisable(GL11.GL_BLEND);
}
private static int worldMinHeight(World world) {
if (NEIModContainer.isCCLoaded() && world instanceof IMinMaxHeight worldHeights) {
return worldHeights.getMinHeight();
} else {
return 0;
}
}
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() {
KeyManager.trackers.add(new WorldOverlayRenderer());
}
+58 -18
View File
@@ -1,14 +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;
@@ -19,18 +23,18 @@ 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;
import codechicken.nei.SearchField;
import codechicken.nei.SearchField.ISearchProvider;
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
import codechicken.nei.SubsetWidget;
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;
@@ -41,6 +45,7 @@ import codechicken.nei.recipe.IUsageHandler;
import codechicken.nei.recipe.RecipeCatalysts;
import codechicken.nei.recipe.RecipeInfo;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.ItemStackFilterParser;
/**
* This is the main class that handles item property configuration. WARNING: DO NOT access this class until the world
@@ -143,23 +148,53 @@ 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 synchronized void hideItem(String rule) {
ItemFilter filter = ItemStackFilterParser.parse(rule);
if (filter != null) {
ItemInfo.hiddenItemsRules.filters.add(filter);
LayoutManager.markItemsDirty();
}
}
/**
* Add or replace the name normally shown on the item tooltip
*/
public static void setOverrideName(ItemStack item, String name) {
if (!name.equals(ItemInfo.nameOverrides.get(item))) {
ItemInfo.nameOverrides.put(item, name);
String existing = ItemInfo.nameOverrides.get(item);
if (!Objects.equals(name, existing)) {
if (name == null) {
ItemInfo.nameOverrides.remove(item);
} else {
ItemInfo.nameOverrides.put(item, name);
}
LayoutManager.markItemsDirty();
}
}
/**
* 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.
*
@@ -190,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) {
@@ -314,11 +341,14 @@ public class API {
SubsetWidget.addTag(tag);
}
@Deprecated
public static void addSearchProvider(ISearchProvider provider) {}
/**
* Adds a new search provider to the search field
*/
public static void addSearchProvider(ISearchProvider provider) {
SearchField.searchProviders.add(provider);
public static void addSearchProvider(ISearchParserProvider provider) {
SearchField.searchParser.addProvider(provider);
}
/**
@@ -349,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
@@ -1,5 +1,7 @@
package codechicken.nei.api;
import static codechicken.lib.gui.GuiDraw.drawRect;
import java.util.ArrayList;
import java.util.List;
@@ -7,13 +9,14 @@ import net.minecraft.inventory.Slot;
import org.lwjgl.opengl.GL11;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.guihook.GuiContainerManager;
public class DefaultOverlayRenderer implements IRecipeOverlayRenderer {
public DefaultOverlayRenderer(List<PositionedStack> ai, IStackPositioner positioner) {
positioner = this.positioner = positioner;
this.positioner = positioner;
ingreds = new ArrayList<>();
for (PositionedStack stack : ai) ingreds.add(stack.copy());
ingreds = positioner.positionStacks(ingreds);
@@ -21,20 +24,22 @@ public class DefaultOverlayRenderer implements IRecipeOverlayRenderer {
@Override
public void renderOverlay(GuiContainerManager gui, Slot slot) {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(770, 1);
GL11.glColor4d(0.6, 0.6, 0.6, 0.7);
GuiContainerManager.setColouredItemRender(true);
for (PositionedStack stack : ingreds) {
if (stack.relx == slot.xDisplayPosition && stack.rely == slot.yDisplayPosition)
if (stack.relx == slot.xDisplayPosition && stack.rely == slot.yDisplayPosition && !slot.getHasStack()) {
GuiContainerManager.drawItem(stack.relx, stack.rely, stack.item);
drawHover(stack.relx, stack.rely);
}
}
GuiContainerManager.setColouredItemRender(false);
GL11.glColor4f(1, 1, 1, 1);
GL11.glDisable(GL11.GL_BLEND);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glPopAttrib();
}
private static void drawHover(int x, int y) {
NEIClientUtils.gl2DRenderContext(() -> drawRect(x, y, 16, 16, 0x66555555));
}
final IStackPositioner positioner;
+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;
}
}
@@ -1,11 +1,26 @@
package codechicken.nei.api;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
public interface IBookmarkContainerHandler {
default List<ItemStack> getStorageStacks(GuiContainer guiContainer) {
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
final List<ItemStack> stacks = new ArrayList<>();
for (int i = 0; i < slots.size() - 4 * 9; i++) {
if (slots.get(i).getHasStack()) {
stacks.add(slots.get(i).getStack().copy());
}
}
return stacks;
}
void pullBookmarkItemsFromContainer(GuiContainer guiContainer, ArrayList<ItemStack> realItems);
}
Loaded 100 of 317 files, more files were not shown because too many files have changed in this diff. Show more