├── .github
└── workflows
│ ├── gradle.yml
│ └── resource.yml
├── .gitignore
├── LICENSE.txt
├── README.md
├── build.gradle.kts
├── discord-bot
├── README.md
├── package.json
└── src
│ ├── bazaar
│ ├── bazaar.js
│ ├── bazaarConversions.json
│ └── performance.js
│ └── index.js
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── log4j2.xml
├── settings.gradle.kts
├── src
└── main
│ ├── java
│ └── dev
│ │ └── meyi
│ │ └── bn
│ │ ├── BazaarNotifier.java
│ │ ├── commands
│ │ └── BazaarNotifierCommand.java
│ │ ├── config
│ │ └── Configuration.java
│ │ ├── handlers
│ │ ├── ChestTickHandler.java
│ │ ├── EventHandler.java
│ │ ├── MouseHandler.java
│ │ └── UpdateHandler.java
│ │ ├── json
│ │ ├── Exchange.java
│ │ ├── Order.java
│ │ └── resp
│ │ │ ├── BazaarItem.java
│ │ │ └── BazaarResponse.java
│ │ ├── modules
│ │ ├── Module.java
│ │ ├── ModuleList.java
│ │ ├── ModuleName.java
│ │ ├── calc
│ │ │ ├── BankCalculator.java
│ │ │ ├── CraftingCalculator.java
│ │ │ └── SuggestionCalculator.java
│ │ └── module
│ │ │ ├── BankModule.java
│ │ │ ├── CraftingModule.java
│ │ │ ├── NotificationModule.java
│ │ │ └── SuggestionModule.java
│ │ └── utilities
│ │ ├── ColoredText.java
│ │ ├── Defaults.java
│ │ ├── ReflectionHelper.java
│ │ ├── RenderUtils.java
│ │ ├── ScheduledEvents.java
│ │ └── Utils.java
│ └── resources
│ ├── icon.png
│ └── mcmod.info
└── wrapper
├── gradle-wrapper.jar
└── gradle-wrapper.properties
/.github/workflows/gradle.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub.
2 | # They are provided by a third-party and are governed by
3 | # separate terms of service, privacy policy, and support
4 | # documentation.
5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
7 |
8 | name: Build JARs
9 |
10 | on:
11 | push:
12 | branches: [ "master" ]
13 | pull_request:
14 | branches: [ "master", "beta" ]
15 |
16 | permissions:
17 | contents: read
18 |
19 | jobs:
20 | build:
21 |
22 | runs-on: ubuntu-latest
23 |
24 | steps:
25 | - uses: actions/checkout@v3
26 | - name: Set up JDK 17
27 | uses: actions/setup-java@v3
28 | with:
29 | java-version: '17'
30 | distribution: 'temurin'
31 | - name: Build with Gradle
32 | uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
33 | with:
34 | arguments: build
35 | - name: Upload JARs
36 | uses: actions/upload-artifact@v4
37 | with:
38 | name: JARs
39 | path: build/libs
40 |
--------------------------------------------------------------------------------
/.github/workflows/resource.yml:
--------------------------------------------------------------------------------
1 | name: 'Update Resources'
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: "0 0 * * 0"
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout repository
13 | uses: actions/checkout@v4
14 | with:
15 | ref: resources
16 | - name: Use Node.js
17 | uses: actions/setup-node@v4
18 | with:
19 | node-version: '20.x'
20 | - name: Build resources.json
21 | env:
22 | KEY: ${{ secrets.KEY }}
23 | run: |
24 | cd sync
25 | npm ci
26 | npm run build --if-present
27 | npm start
28 | - name: Create Pull Request
29 | uses: peter-evans/create-pull-request@v6
30 | with:
31 | commit-message: Automated update of resources.json
32 | title: Updating resources branch with latest version of resources.json
33 | body: This is entirely automated. If something doesn't look right, fix it!
34 | branch: update-resources
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # eclipse
2 | bin
3 | *.launch
4 | .settings
5 | .metadata
6 | .classpath
7 | .project
8 |
9 | # idea
10 | out
11 | *.ipr
12 | *.iws
13 | *.iml
14 | .idea
15 |
16 | # gradle
17 | build
18 | .gradle
19 |
20 | # other
21 | eclipse
22 | run
23 | .vscode
24 |
25 | .env
26 | node_modules
27 | .DS_STORE
28 |
29 | # resources file (downloaded from github on build)
30 | src/main/resources/resources.json
31 |
32 | sync
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | Attribution-NonCommercial-ShareAlike 3.0 Unported
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
10 | DAMAGES RESULTING FROM ITS USE.
11 |
12 | License
13 |
14 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
15 | COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
16 | COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
17 | AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
18 |
19 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
20 | TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
21 | BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
22 | CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
23 | CONDITIONS.
24 |
25 | 1. Definitions
26 |
27 | a. "Adaptation" means a work based upon the Work, or upon the Work and
28 | other pre-existing works, such as a translation, adaptation,
29 | derivative work, arrangement of music or other alterations of a
30 | literary or artistic work, or phonogram or performance and includes
31 | cinematographic adaptations or any other form in which the Work may be
32 | recast, transformed, or adapted including in any form recognizably
33 | derived from the original, except that a work that constitutes a
34 | Collection will not be considered an Adaptation for the purpose of
35 | this License. For the avoidance of doubt, where the Work is a musical
36 | work, performance or phonogram, the synchronization of the Work in
37 | timed-relation with a moving image ("synching") will be considered an
38 | Adaptation for the purpose of this License.
39 | b. "Collection" means a collection of literary or artistic works, such as
40 | encyclopedias and anthologies, or performances, phonograms or
41 | broadcasts, or other works or subject matter other than works listed
42 | in Section 1(g) below, which, by reason of the selection and
43 | arrangement of their contents, constitute intellectual creations, in
44 | which the Work is included in its entirety in unmodified form along
45 | with one or more other contributions, each constituting separate and
46 | independent works in themselves, which together are assembled into a
47 | collective whole. A work that constitutes a Collection will not be
48 | considered an Adaptation (as defined above) for the purposes of this
49 | License.
50 | c. "Distribute" means to make available to the public the original and
51 | copies of the Work or Adaptation, as appropriate, through sale or
52 | other transfer of ownership.
53 | d. "License Elements" means the following high-level license attributes
54 | as selected by Licensor and indicated in the title of this License:
55 | Attribution, Noncommercial, ShareAlike.
56 | e. "Licensor" means the individual, individuals, entity or entities that
57 | offer(s) the Work under the terms of this License.
58 | f. "Original Author" means, in the case of a literary or artistic work,
59 | the individual, individuals, entity or entities who created the Work
60 | or if no individual or entity can be identified, the publisher; and in
61 | addition (i) in the case of a performance the actors, singers,
62 | musicians, dancers, and other persons who act, sing, deliver, declaim,
63 | play in, interpret or otherwise perform literary or artistic works or
64 | expressions of folklore; (ii) in the case of a phonogram the producer
65 | being the person or legal entity who first fixes the sounds of a
66 | performance or other sounds; and, (iii) in the case of broadcasts, the
67 | organization that transmits the broadcast.
68 | g. "Work" means the literary and/or artistic work offered under the terms
69 | of this License including without limitation any production in the
70 | literary, scientific and artistic domain, whatever may be the mode or
71 | form of its expression including digital form, such as a book,
72 | pamphlet and other writing; a lecture, address, sermon or other work
73 | of the same nature; a dramatic or dramatico-musical work; a
74 | choreographic work or entertainment in dumb show; a musical
75 | composition with or without words; a cinematographic work to which are
76 | assimilated works expressed by a process analogous to cinematography;
77 | a work of drawing, painting, architecture, sculpture, engraving or
78 | lithography; a photographic work to which are assimilated works
79 | expressed by a process analogous to photography; a work of applied
80 | art; an illustration, map, plan, sketch or three-dimensional work
81 | relative to geography, topography, architecture or science; a
82 | performance; a broadcast; a phonogram; a compilation of data to the
83 | extent it is protected as a copyrightable work; or a work performed by
84 | a variety or circus performer to the extent it is not otherwise
85 | considered a literary or artistic work.
86 | h. "You" means an individual or entity exercising rights under this
87 | License who has not previously violated the terms of this License with
88 | respect to the Work, or who has received express permission from the
89 | Licensor to exercise rights under this License despite a previous
90 | violation.
91 | i. "Publicly Perform" means to perform public recitations of the Work and
92 | to communicate to the public those public recitations, by any means or
93 | process, including by wire or wireless means or public digital
94 | performances; to make available to the public Works in such a way that
95 | members of the public may access these Works from a place and at a
96 | place individually chosen by them; to perform the Work to the public
97 | by any means or process and the communication to the public of the
98 | performances of the Work, including by public digital performance; to
99 | broadcast and rebroadcast the Work by any means including signs,
100 | sounds or images.
101 | j. "Reproduce" means to make copies of the Work by any means including
102 | without limitation by sound or visual recordings and the right of
103 | fixation and reproducing fixations of the Work, including storage of a
104 | protected performance or phonogram in digital form or other electronic
105 | medium.
106 |
107 | 2. Fair Dealing Rights. Nothing in this License is intended to reduce,
108 | limit, or restrict any uses free from copyright or rights arising from
109 | limitations or exceptions that are provided for in connection with the
110 | copyright protection under copyright law or other applicable laws.
111 |
112 | 3. License Grant. Subject to the terms and conditions of this License,
113 | Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
114 | perpetual (for the duration of the applicable copyright) license to
115 | exercise the rights in the Work as stated below:
116 |
117 | a. to Reproduce the Work, to incorporate the Work into one or more
118 | Collections, and to Reproduce the Work as incorporated in the
119 | Collections;
120 | b. to create and Reproduce Adaptations provided that any such Adaptation,
121 | including any translation in any medium, takes reasonable steps to
122 | clearly label, demarcate or otherwise identify that changes were made
123 | to the original Work. For example, a translation could be marked "The
124 | original work was translated from English to Spanish," or a
125 | modification could indicate "The original work has been modified.";
126 | c. to Distribute and Publicly Perform the Work including as incorporated
127 | in Collections; and,
128 | d. to Distribute and Publicly Perform Adaptations.
129 |
130 | The above rights may be exercised in all media and formats whether now
131 | known or hereafter devised. The above rights include the right to make
132 | such modifications as are technically necessary to exercise the rights in
133 | other media and formats. Subject to Section 8(f), all rights not expressly
134 | granted by Licensor are hereby reserved, including but not limited to the
135 | rights described in Section 4(e).
136 |
137 | 4. Restrictions. The license granted in Section 3 above is expressly made
138 | subject to and limited by the following restrictions:
139 |
140 | a. You may Distribute or Publicly Perform the Work only under the terms
141 | of this License. You must include a copy of, or the Uniform Resource
142 | Identifier (URI) for, this License with every copy of the Work You
143 | Distribute or Publicly Perform. You may not offer or impose any terms
144 | on the Work that restrict the terms of this License or the ability of
145 | the recipient of the Work to exercise the rights granted to that
146 | recipient under the terms of the License. You may not sublicense the
147 | Work. You must keep intact all notices that refer to this License and
148 | to the disclaimer of warranties with every copy of the Work You
149 | Distribute or Publicly Perform. When You Distribute or Publicly
150 | Perform the Work, You may not impose any effective technological
151 | measures on the Work that restrict the ability of a recipient of the
152 | Work from You to exercise the rights granted to that recipient under
153 | the terms of the License. This Section 4(a) applies to the Work as
154 | incorporated in a Collection, but this does not require the Collection
155 | apart from the Work itself to be made subject to the terms of this
156 | License. If You create a Collection, upon notice from any Licensor You
157 | must, to the extent practicable, remove from the Collection any credit
158 | as required by Section 4(d), as requested. If You create an
159 | Adaptation, upon notice from any Licensor You must, to the extent
160 | practicable, remove from the Adaptation any credit as required by
161 | Section 4(d), as requested.
162 | b. You may Distribute or Publicly Perform an Adaptation only under: (i)
163 | the terms of this License; (ii) a later version of this License with
164 | the same License Elements as this License; (iii) a Creative Commons
165 | jurisdiction license (either this or a later license version) that
166 | contains the same License Elements as this License (e.g.,
167 | Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License").
168 | You must include a copy of, or the URI, for Applicable License with
169 | every copy of each Adaptation You Distribute or Publicly Perform. You
170 | may not offer or impose any terms on the Adaptation that restrict the
171 | terms of the Applicable License or the ability of the recipient of the
172 | Adaptation to exercise the rights granted to that recipient under the
173 | terms of the Applicable License. You must keep intact all notices that
174 | refer to the Applicable License and to the disclaimer of warranties
175 | with every copy of the Work as included in the Adaptation You
176 | Distribute or Publicly Perform. When You Distribute or Publicly
177 | Perform the Adaptation, You may not impose any effective technological
178 | measures on the Adaptation that restrict the ability of a recipient of
179 | the Adaptation from You to exercise the rights granted to that
180 | recipient under the terms of the Applicable License. This Section 4(b)
181 | applies to the Adaptation as incorporated in a Collection, but this
182 | does not require the Collection apart from the Adaptation itself to be
183 | made subject to the terms of the Applicable License.
184 | c. You may not exercise any of the rights granted to You in Section 3
185 | above in any manner that is primarily intended for or directed toward
186 | commercial advantage or private monetary compensation. The exchange of
187 | the Work for other copyrighted works by means of digital file-sharing
188 | or otherwise shall not be considered to be intended for or directed
189 | toward commercial advantage or private monetary compensation, provided
190 | there is no payment of any monetary compensation in con-nection with
191 | the exchange of copyrighted works.
192 | d. If You Distribute, or Publicly Perform the Work or any Adaptations or
193 | Collections, You must, unless a request has been made pursuant to
194 | Section 4(a), keep intact all copyright notices for the Work and
195 | provide, reasonable to the medium or means You are utilizing: (i) the
196 | name of the Original Author (or pseudonym, if applicable) if supplied,
197 | and/or if the Original Author and/or Licensor designate another party
198 | or parties (e.g., a sponsor institute, publishing entity, journal) for
199 | attribution ("Attribution Parties") in Licensor's copyright notice,
200 | terms of service or by other reasonable means, the name of such party
201 | or parties; (ii) the title of the Work if supplied; (iii) to the
202 | extent reasonably practicable, the URI, if any, that Licensor
203 | specifies to be associated with the Work, unless such URI does not
204 | refer to the copyright notice or licensing information for the Work;
205 | and, (iv) consistent with Section 3(b), in the case of an Adaptation,
206 | a credit identifying the use of the Work in the Adaptation (e.g.,
207 | "French translation of the Work by Original Author," or "Screenplay
208 | based on original Work by Original Author"). The credit required by
209 | this Section 4(d) may be implemented in any reasonable manner;
210 | provided, however, that in the case of a Adaptation or Collection, at
211 | a minimum such credit will appear, if a credit for all contributing
212 | authors of the Adaptation or Collection appears, then as part of these
213 | credits and in a manner at least as prominent as the credits for the
214 | other contributing authors. For the avoidance of doubt, You may only
215 | use the credit required by this Section for the purpose of attribution
216 | in the manner set out above and, by exercising Your rights under this
217 | License, You may not implicitly or explicitly assert or imply any
218 | connection with, sponsorship or endorsement by the Original Author,
219 | Licensor and/or Attribution Parties, as appropriate, of You or Your
220 | use of the Work, without the separate, express prior written
221 | permission of the Original Author, Licensor and/or Attribution
222 | Parties.
223 | e. For the avoidance of doubt:
224 |
225 | i. Non-waivable Compulsory License Schemes. In those jurisdictions in
226 | which the right to collect royalties through any statutory or
227 | compulsory licensing scheme cannot be waived, the Licensor
228 | reserves the exclusive right to collect such royalties for any
229 | exercise by You of the rights granted under this License;
230 | ii. Waivable Compulsory License Schemes. In those jurisdictions in
231 | which the right to collect royalties through any statutory or
232 | compulsory licensing scheme can be waived, the Licensor reserves
233 | the exclusive right to collect such royalties for any exercise by
234 | You of the rights granted under this License if Your exercise of
235 | such rights is for a purpose or use which is otherwise than
236 | noncommercial as permitted under Section 4(c) and otherwise waives
237 | the right to collect royalties through any statutory or compulsory
238 | licensing scheme; and,
239 | iii. Voluntary License Schemes. The Licensor reserves the right to
240 | collect royalties, whether individually or, in the event that the
241 | Licensor is a member of a collecting society that administers
242 | voluntary licensing schemes, via that society, from any exercise
243 | by You of the rights granted under this License that is for a
244 | purpose or use which is otherwise than noncommercial as permitted
245 | under Section 4(c).
246 | f. Except as otherwise agreed in writing by the Licensor or as may be
247 | otherwise permitted by applicable law, if You Reproduce, Distribute or
248 | Publicly Perform the Work either by itself or as part of any
249 | Adaptations or Collections, You must not distort, mutilate, modify or
250 | take other derogatory action in relation to the Work which would be
251 | prejudicial to the Original Author's honor or reputation. Licensor
252 | agrees that in those jurisdictions (e.g. Japan), in which any exercise
253 | of the right granted in Section 3(b) of this License (the right to
254 | make Adaptations) would be deemed to be a distortion, mutilation,
255 | modification or other derogatory action prejudicial to the Original
256 | Author's honor and reputation, the Licensor will waive or not assert,
257 | as appropriate, this Section, to the fullest extent permitted by the
258 | applicable national law, to enable You to reasonably exercise Your
259 | right under Section 3(b) of this License (right to make Adaptations)
260 | but not otherwise.
261 |
262 | 5. Representations, Warranties and Disclaimer
263 |
264 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE
265 | FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS
266 | AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
267 | WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
268 | LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
269 | PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
270 | ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
271 | DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
272 | WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
273 |
274 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
275 | LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
276 | ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
277 | ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
278 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
279 |
280 | 7. Termination
281 |
282 | a. This License and the rights granted hereunder will terminate
283 | automatically upon any breach by You of the terms of this License.
284 | Individuals or entities who have received Adaptations or Collections
285 | from You under this License, however, will not have their licenses
286 | terminated provided such individuals or entities remain in full
287 | compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
288 | survive any termination of this License.
289 | b. Subject to the above terms and conditions, the license granted here is
290 | perpetual (for the duration of the applicable copyright in the Work).
291 | Notwithstanding the above, Licensor reserves the right to release the
292 | Work under different license terms or to stop distributing the Work at
293 | any time; provided, however that any such election will not serve to
294 | withdraw this License (or any other license that has been, or is
295 | required to be, granted under the terms of this License), and this
296 | License will continue in full force and effect unless terminated as
297 | stated above.
298 |
299 | 8. Miscellaneous
300 |
301 | a. Each time You Distribute or Publicly Perform the Work or a Collection,
302 | the Licensor offers to the recipient a license to the Work on the same
303 | terms and conditions as the license granted to You under this License.
304 | b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
305 | offers to the recipient a license to the original Work on the same
306 | terms and conditions as the license granted to You under this License.
307 | c. If any provision of this License is invalid or unenforceable under
308 | applicable law, it shall not affect the validity or enforceability of
309 | the remainder of the terms of this License, and without further action
310 | by the parties to this agreement, such provision shall be reformed to
311 | the minimum extent necessary to make such provision valid and
312 | enforceable.
313 | d. No term or provision of this License shall be deemed waived and no
314 | breach consented to unless such waiver or consent shall be in writing
315 | and signed by the party to be charged with such waiver or consent.
316 | e. This License constitutes the entire agreement between the parties with
317 | respect to the Work licensed here. There are no understandings,
318 | agreements or representations with respect to the Work not specified
319 | here. Licensor shall not be bound by any additional provisions that
320 | may appear in any communication from You. This License may not be
321 | modified without the mutual written agreement of the Licensor and You.
322 | f. The rights granted under, and the subject matter referenced, in this
323 | License were drafted utilizing the terminology of the Berne Convention
324 | for the Protection of Literary and Artistic Works (as amended on
325 | September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
326 | Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
327 | and the Universal Copyright Convention (as revised on July 24, 1971).
328 | These rights and subject matter take effect in the relevant
329 | jurisdiction in which the License terms are sought to be enforced
330 | according to the corresponding provisions of the implementation of
331 | those treaty provisions in the applicable national law. If the
332 | standard suite of rights granted under applicable copyright law
333 | includes additional rights not granted under this License, such
334 | additional rights are deemed to be included in the License; this
335 | License is not intended to restrict the license of any rights under
336 | applicable law.
337 |
338 |
339 | Creative Commons Notice
340 |
341 | Creative Commons is not a party to this License, and makes no warranty
342 | whatsoever in connection with the Work. Creative Commons will not be
343 | liable to You or any party on any legal theory for any damages
344 | whatsoever, including without limitation any general, special,
345 | incidental or consequential damages arising in connection to this
346 | license. Notwithstanding the foregoing two (2) sentences, if Creative
347 | Commons has expressly identified itself as the Licensor hereunder, it
348 | shall have all rights and obligations of Licensor.
349 |
350 | Except for the limited purpose of indicating to the public that the
351 | Work is licensed under the CCPL, Creative Commons does not authorize
352 | the use by either party of the trademark "Creative Commons" or any
353 | related trademark or logo of Creative Commons without the prior
354 | written consent of Creative Commons. Any permitted use will be in
355 | compliance with Creative Commons' then-current trademark usage
356 | guidelines, as may be published on its website or otherwise made
357 | available upon request from time to time. For the avoidance of doubt,
358 | this trademark restriction does not form part of this License.
359 |
360 | Creative Commons may be contacted at https://creativecommons.org/.
361 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BazaarNotifier
2 |
3 | Mod and Discord Bot for Hypixel Skyblock that helps with tracking bazaar sell offer and buy order movements
4 |
5 |
6 |
7 | All bug reports should be in [issues](https://github.com/symt/BazaarNotifier/issues) or in the discord server.
8 |
9 |
10 | ## Building
11 |
12 | This project is now built on [this template](https://github.com/romangraef/Forge1.8.9Template/). Build instructions from there:
13 |
14 | > To run the mod you will need two JDKs, one Java 17 jdk and one Java 1.8 jdk.
15 |
16 | > When you import your project into IntelliJ, you need to set the gradle jvm to the Java 17 JDK in the gradle tab, and the Project SDK to the Java 1.8 JDK. Then click on the sync button in IntelliJ, and it should create a run task called Minecraft Client. If it doesn't then try relaunching your IntelliJ. Warning for Mac users: You might have to remove the -XStartOnFirstThread vm argument from your run configuration.
17 |
18 | You can also build in the command line. Set your JAVA_HOME environment variable to the path to the 1.8 JDK and then run
19 |
20 | ```
21 | ./gradlew build -Dorg.gradle.java.home=/PATH/TO/JDK17
22 | ```
23 |
24 | ## Extras
25 |
26 | Join the discord server: https://discord.com/invite/wjpJSVSwvD
27 |
28 |
29 |
30 |
31 | [License](https://github.com/symt/BazaarNotifier/blob/master/license.txt) | [Site](https://meyi.dev)
32 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import de.undercouch.gradle.tasks.download.Download
2 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
3 |
4 | plugins {
5 | idea
6 | java
7 | id("gg.essential.loom") version "0.10.0.+"
8 | id("dev.architectury.architectury-pack200") version "0.1.3"
9 | id("de.undercouch.download").version("5.3.0")
10 | id("com.github.johnrengelman.shadow") version "7.1.2"
11 |
12 | }
13 |
14 | group = "dev.meyi.bazaarnotifier"
15 | version = "1.7.5"
16 | val mod_id = "bazaarnotifier"
17 |
18 | java {
19 | toolchain.languageVersion.set(JavaLanguageVersion.of(8))
20 | }
21 |
22 | loom {
23 | launchConfigs.named("client") {
24 | arg("--tweakClass", "cc.polyfrost.oneconfig.loader.stage0.LaunchWrapperTweaker")
25 | }
26 | log4jConfigs.from(file("log4j2.xml"))
27 | forge {
28 | pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter())
29 | }
30 | }
31 |
32 | sourceSets.main {
33 | output.setResourcesDir(file("$buildDir/classes/java/main"))
34 | }
35 |
36 | val shade: Configuration by configurations.creating {
37 | configurations.implementation.get().extendsFrom(this)
38 | }
39 |
40 | repositories {
41 | mavenCentral()
42 | maven("https://repo.spongepowered.org/maven/")
43 | maven("https://repo.polyfrost.cc/releases")
44 | }
45 |
46 | dependencies {
47 | minecraft("com.mojang:minecraft:1.8.9")
48 | mappings("de.oceanlabs.mcp:mcp_stable:22-1.8.9")
49 | forge("net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9")
50 | modCompileOnly("cc.polyfrost:oneconfig-1.8.9-forge:0.2.2-alpha+")
51 | shade("cc.polyfrost:oneconfig-wrapper-launchwrapper:1.0.0-beta17")
52 | }
53 |
54 |
55 |
56 | val resourcesFile = "src/main/resources/resources.json"
57 | val resourcesURL = "https://raw.githubusercontent.com/symt/BazaarNotifier/resources/resources.json"
58 |
59 | tasks.processResources {
60 | duplicatesStrategy = DuplicatesStrategy.INCLUDE
61 |
62 | inputs.property("version", project.version)
63 | inputs.property("mcversion", "1.8.9")
64 | from(sourceSets["main"].resources.srcDirs) {
65 | include("mcmod.info")
66 | expand("version" to project.version, "mcversion" to "1.8.9")
67 | }
68 | from(sourceSets["main"].resources.srcDirs) {
69 | exclude("mcmod.info")
70 | }
71 |
72 | dependsOn("retrieveResources")
73 | finalizedBy("destroyResources")
74 | outputs.upToDateWhen { false }
75 | }
76 |
77 | task("destroyResources") {
78 | doLast {
79 | if (File(resourcesFile).exists()) {
80 | project.delete(files(resourcesFile))
81 | }
82 | }
83 | outputs.upToDateWhen { false }
84 | }
85 |
86 | task("retrieveResources") {
87 | val dest = File(resourcesFile)
88 |
89 | if (dest.exists()) {
90 | project.delete(files(resourcesFile))
91 | }
92 | task("download-task") {
93 | src(resourcesURL)
94 | dest(resourcesFile)
95 | }
96 | dependsOn("download-task")
97 | }
98 |
99 | tasks{
100 | withType(JavaCompile::class) {
101 | options.encoding = "UTF-8"
102 | }
103 | withType(Jar::class) {
104 | manifest.attributes.run {
105 | this["FMLCorePluginContainsFMLMod"] = "true"
106 | this["ForceLoadAsMod"] = "true"
107 | }
108 | named("shadowJar") {
109 | archiveClassifier.set("dev")
110 | configurations = listOf(shade)
111 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE
112 | }
113 |
114 | remapJar {
115 | input.set(shadowJar.get().archiveFile)
116 | archiveClassifier.set("")
117 | }
118 |
119 | jar {
120 | manifest {
121 | attributes(
122 | mapOf(
123 | "ModSide" to "CLIENT",
124 | "ForceLoadAsMod" to true,
125 | "TweakOrder" to "0",
126 | "TweakClass" to "cc.polyfrost.oneconfig.loader.stage0.LaunchWrapperTweaker"
127 | )
128 | )
129 | }
130 | dependsOn(shadowJar)
131 | archiveClassifier.set("")
132 | enabled = false
133 | }
134 | processResources {
135 | duplicatesStrategy = DuplicatesStrategy.INCLUDE
136 |
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/discord-bot/README.md:
--------------------------------------------------------------------------------
1 | ## Bazaar Bot
2 |
3 |
4 | This is the bot that's used at the discord server: https://discord.gg/sjNFags
5 |
6 | Currently, it should work in every server. The `~find` command, however, hasn't been implemented to work for every server yet. You can implement it for other servers by changing the channel id, but it won't work if the instance isn't yours.
7 |
8 | To setup, you need a .env file for storing the hypixel api key and the bot's token:
9 | ```
10 | HYPIXEL_API_KEY=___________________________
11 | BOT=_______________________
12 | CHANNEL=______________
13 | ```
14 |
15 | To set the channel in which the bot spits out the information, go into index.js and change the channel variable to the id of the channel of your choosing. You can also modify the .env file as seen above. The prefix, defaulting to `~`, can be set in index.js.
16 |
17 | If you want to merge the bot with your own, change the prefix, client and channel in the import variables of bazaar/bazaar.js to your own and run cacheUpdate function in your ready event (it will automatically run the rest of the files).
18 |
19 |
--------------------------------------------------------------------------------
/discord-bot/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bazaar-bot",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "./src/index.js",
6 | "scripts": {
7 | "start": "node ./src/index.js"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "axios": "^0.27.2",
14 | "discord.js": "^13.7.0",
15 | "dotenv": "^8.2.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/discord-bot/src/bazaar/bazaar.js:
--------------------------------------------------------------------------------
1 | const {MessageEmbed} = require('discord.js');
2 | const {client, prefix, channel} = require('../index');
3 | const axios = require("axios").default;
4 | const bazaarConversions = require("./bazaarConversions.json");
5 | const performance = require("./performance.js");
6 |
7 | const toTitleCase = (phrase) => {
8 | return phrase
9 | .toLowerCase()
10 | .split(" ")
11 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
12 | .join(" ");
13 | };
14 |
15 | let inRequest = false;
16 | let bazaarCache = {};
17 |
18 | client.on("messageCreate", (message) => {
19 | if (
20 | !message.content.startsWith(prefix) ||
21 | !message.channel.id === "733213711157821452"
22 | ) {
23 | return;
24 | }
25 |
26 | if (message.content.startsWith(prefix + "find") && message.content.split(
27 | " ").length >= 2) {
28 |
29 | let item = message.content.slice(6).toLowerCase().trim();
30 | if (bazaarCache[item]) {
31 | let data = bazaarCache[item];
32 | message.channel.send({
33 | embeds: [
34 | new MessageEmbed()
35 | .setTitle(toTitleCase(item))
36 | .setColor(Math.floor(Math.random() * 16777215))
37 | .setDescription(
38 | `**Buy Order**: ${data.buyOrderPrice}\n**Sell Order**: ${
39 | Math.round(data.sellOrderPrice * 99) / 100
40 | }\n**Difference**: ${
41 | Math.round(
42 | (data.sellOrderPrice * 0.99 - data.buyOrderPrice) * 100
43 | ) / 100
44 | }\n**Estimated Profit (coins/minute)**: ${
45 | Math.round(data.profitFlowPerMinute * 100) / 100
46 | }`
47 | )
48 | ]
49 | });
50 | } else {
51 | message.reply({
52 | content: `The item searched (**${item}**) doesn't exist. Maybe something went wrong on my end, but odds are you just messed up!`
53 | });
54 | }
55 | }
56 | });
57 |
58 | const cacheUpdate = async () => {
59 | try {
60 | if (client && !inRequest) {
61 | inRequest = true;
62 | let bazaarData = [];
63 | let key = process.env.HYPIXEL_API_KEY;
64 | await axios({
65 | method: "GET",
66 | url: `https://api.hypixel.net/skyblock/bazaar?key=${key}`,
67 | })
68 | .then((res) => {
69 | let products = res.data.products;
70 | let productIds = Object.keys(res.data.products);
71 |
72 | productIds.forEach((id) => {
73 | let product = products[id];
74 | bazaarData.push({
75 | productId: bazaarConversions[id] || id,
76 | sellOrderPrice:
77 | product.buy_summary[0] && product.sell_summary[0]
78 | ? product.buy_summary[0].pricePerUnit
79 | : 0,
80 | buyOrderPrice:
81 | product.sell_summary[0] && product.buy_summary[0]
82 | ? product.sell_summary[0].pricePerUnit
83 | : 0,
84 | sellCount: product.quick_status.buyMovingWeek,
85 | buyCount: product.quick_status.sellMovingWeek,
86 | });
87 | });
88 | })
89 | .catch((e) => {
90 | console.log(e);
91 | });
92 |
93 | bazaarData = performance(bazaarData);
94 |
95 | bazaarData.forEach((data) => {
96 | bazaarCache[data.productId.toLowerCase()] = data;
97 | });
98 |
99 | bazaarData = bazaarData.slice(0, 16);
100 |
101 | let fields = [];
102 | let i = 1;
103 | bazaarData.forEach((data) => {
104 | fields.push({
105 | name: `${i++}. ${data.productId}`,
106 | value: `EP: ${Math.round(data.profitFlowPerMinute * 100) / 100}`,
107 | inline: true,
108 | });
109 | if (i % 2 == 0) {
110 | fields.push({name: "\u200B", value: "\u200B", inline: true});
111 | }
112 | });
113 | let embed = new MessageEmbed()
114 | .setTitle("Bazaar")
115 | .setColor(Math.floor(Math.random() * 16777215))
116 | .addFields(...fields)
117 | .setFooter({
118 | text: "EP is about how much money you'd make while flipping an item per minute of flipping. It assumes you miss no instants."
119 | });
120 | client.channels.cache.get(channel).send({embeds: [embed]});
121 | inRequest = false;
122 | }
123 | } catch (e) {
124 | inRequest = false;
125 | console.log(e);
126 | }
127 | }
128 |
129 | setInterval(cacheUpdate, 30000);
130 |
131 | module.exports = {cacheUpdate};
132 |
--------------------------------------------------------------------------------
/discord-bot/src/bazaar/bazaarConversions.json:
--------------------------------------------------------------------------------
1 | {
2 | "ENCHANTED_RAW_CHICKEN": "Enchanted Raw Chicken",
3 | "INK_SACK:3": "Cocoa Beans",
4 | "BROWN_MUSHROOM": "Brown Mushroom",
5 | "ENCHANTED_WATER_LILY": "Enchanted Lily Pad",
6 | "INK_SACK:4": "Lapis Lazuli",
7 | "TARANTULA_WEB": "Tarantula Web",
8 | "CARROT_ITEM": "Carrot",
9 | "ENCHANTED_POTATO": "Enchanted Potato",
10 | "LOG:1": "Spruce Log",
11 | "ENCHANTED_SLIME_BALL": "Enchanted Slime Ball",
12 | "ENCHANTED_GOLDEN_CARROT": "Enchanted Golden Carrot",
13 | "LOG:3": "Jungle Log",
14 | "LOG:2": "Birch Log",
15 | "ENCHANTED_RABBIT_HIDE": "Enchanted Rabbit Hide",
16 | "ENCHANTED_GLOWSTONE_DUST": "Enchanted Glowstone Dust",
17 | "ENCHANTED_INK_SACK": "Enchanted Ink Sack",
18 | "ENCHANTED_CACTUS": "Enchanted Cactus",
19 | "ENCHANTED_SUGAR_CANE": "Enchanted Sugar Cane",
20 | "ENCHANTED_BIRCH_LOG": "Enchanted Birch Log",
21 | "ENCHANTED_GUNPOWDER": "Enchanted Gunpowder",
22 | "ENCHANTED_MELON": "Enchanted Melon",
23 | "ENCHANTED_COOKED_SALMON": "Enchanted Cooked Salmon",
24 | "ENCHANTED_SUGAR": "Enchanted Sugar",
25 | "LOG": "Oak Log",
26 | "CACTUS": "Cactus",
27 | "ENCHANTED_BLAZE_ROD": "Enchanted Blaze Rod",
28 | "GHAST_TEAR": "Ghast Tear",
29 | "ENCHANTED_CAKE": "Enchanted Cake",
30 | "PUMPKIN": "Pumpkin",
31 | "ENCHANTED_ENDER_PEARL": "Enchanted Ender Pearl",
32 | "PURPLE_CANDY": "Purple Candy",
33 | "WHEAT": "Wheat",
34 | "ENCHANTED_FERMENTED_SPIDER_EYE": "Enchanted Fermented Spider Eye",
35 | "ENCHANTED_GOLD_BLOCK": "Enchanted Gold Block",
36 | "ENCHANTED_RAW_SALMON": "Enchanted Raw Salmon",
37 | "ENCHANTED_JUNGLE_LOG": "Enchanted Jungle Log",
38 | "ENCHANTED_FLINT": "Enchanted Flint",
39 | "ENCHANTED_GLISTERING_MELON": "Enchanted Glistering Melon",
40 | "IRON_INGOT": "Iron Ingot",
41 | "PRISMARINE_SHARD": "Prismarine Shard",
42 | "ENCHANTED_EMERALD": "Enchanted Emerald",
43 | "ENCHANTED_SPIDER_EYE": "Enchanted Spider Eye",
44 | "ENCHANTED_EMERALD_BLOCK": "Enchanted Emerald Block",
45 | "RED_MUSHROOM": "Red Mushroom",
46 | "MUTTON": "Mutton",
47 | "ENCHANTED_MELON_BLOCK": "Enchanted Melon Block",
48 | "ENCHANTED_CLAY_BALL": "Enchanted Clay Ball",
49 | "DIAMOND": "Diamond",
50 | "COBBLESTONE": "Cobblestone",
51 | "SPIDER_EYE": "Spider Eye",
52 | "RAW_FISH": "Raw Fish",
53 | "ENCHANTED_PUFFERFISH": "Enchanted Pufferfish",
54 | "GLOWSTONE_DUST": "Glowstone Dust",
55 | "GOLD_INGOT": "Gold Ingot",
56 | "REVENANT_VISCERA": "Revenant Viscera",
57 | "TARANTULA_SILK": "Tarantula Silk",
58 | "POTATO_ITEM": "Potato",
59 | "ENCHANTED_MUTTON": "Enchanted Mutton",
60 | "ENCHANTED_HUGE_MUSHROOM_1": "Enchanted Brown Mushroom Block",
61 | "SUPER_COMPACTOR_3000": "Super Compactor 3000",
62 | "ENCHANTED_IRON": "Enchanted Iron",
63 | "SUPER_EGG": "Super Egg",
64 | "STOCK_OF_STONKS": "Stock Of Stonks",
65 | "ENCHANTED_COBBLESTONE": "Enchanted Cobblestone",
66 | "ENCHANTED_BONE": "Enchanted Bone",
67 | "ENCHANTED_PAPER": "Enchanted Paper",
68 | "ENCHANTED_HUGE_MUSHROOM_2": "Enchanted Red Mushroom Block",
69 | "PORK": "Raw Porkchop",
70 | "ENCHANTED_DIAMOND_BLOCK": "Enchanted Diamond Block",
71 | "EMERALD": "Emerald",
72 | "ENCHANTED_RABBIT_FOOT": "Enchanted Rabbit Foot",
73 | "PRISMARINE_CRYSTALS": "Prismarine Crystals",
74 | "HOT_POTATO_BOOK": "Hot Potato Book",
75 | "ENCHANTED_ICE": "Enchanted Ice",
76 | "ICE": "Ice",
77 | "CLAY_BALL": "Clay Ball",
78 | "HUGE_MUSHROOM_1": "Brown Mushroom Block",
79 | "HUGE_MUSHROOM_2": "Red Mushroom Block",
80 | "LOG_2:1": "Dark Oak Log",
81 | "GREEN_GIFT": "Green Gift",
82 | "ENCHANTED_SNOW_BLOCK": "Enchanted Snow Block",
83 | "GOLDEN_TOOTH": "Golden Tooth",
84 | "STRING": "String",
85 | "PACKED_ICE": "Packed Ice",
86 | "WATER_LILY": "Lily Pad",
87 | "RABBIT_FOOT": "Rabbit's Foot",
88 | "LOG_2": "Acacia Log",
89 | "REDSTONE": "Redstone",
90 | "ENCHANTED_OBSIDIAN": "Enchanted Obsidian",
91 | "ENCHANTED_COAL": "Enchanted Coal",
92 | "COAL": "Coal",
93 | "ENCHANTED_QUARTZ": "Enchanted Quartz",
94 | "ENDER_PEARL": "Ender Pearl",
95 | "ENCHANTED_COAL_BLOCK": "Enchanted Coal Block",
96 | "ENCHANTED_CACTUS_GREEN": "Enchanted Cactus Green",
97 | "ENCHANTED_PRISMARINE_CRYSTALS": "Enchanted Prismarine Crystals",
98 | "ENCHANTED_CARROT_STICK": "Enchanted Carrot On A Stick",
99 | "ENCHANTED_CARROT_ON_A_STICK": "Enchanted Carrot On A Stick",
100 | "ENCHANTED_ENDSTONE": "Enchanted End Stone",
101 | "ENCHANTED_LAPIS_LAZULI_BLOCK": "Enchanted Lapis Lazuli Block",
102 | "ENCHANTED_COOKIE": "Enchanted Cookie",
103 | "ENCHANTED_STRING": "Enchanted String",
104 | "SLIME_BALL": "Slime Ball",
105 | "ENDER_STONE": "End Stone",
106 | "ENCHANTED_RAW_FISH": "Enchanted Raw Fish",
107 | "ENCHANTED_ACACIA_LOG": "Enchanted Acacia Log",
108 | "SNOW_BALL": "Snow Ball",
109 | "ENCHANTED_EGG": "Enchanted Egg",
110 | "QUARTZ": "Nether Quartz",
111 | "RAW_BEEF": "Raw Beef",
112 | "ENCHANTED_EYE_OF_ENDER": "Enchanted Eye of Ender",
113 | "SAND": "Sand",
114 | "RAW_CHICKEN": "Raw Chicken",
115 | "MAGMA_CREAM": "Magma Cream",
116 | "SUGAR_CANE": "Sugar Cane",
117 | "ENCHANTED_LAPIS_LAZULI": "Enchanted Lapis Lazuli",
118 | "ENCHANTED_GHAST_TEAR": "Enchanted Ghast Tear",
119 | "ENCHANTED_COCOA": "Enchanted Cocoa",
120 | "RED_GIFT": "Red Gift",
121 | "ENCHANTED_RAW_BEEF": "Enchanted Raw Beef",
122 | "SEEDS": "Seeds",
123 | "ENCHANTED_LEATHER": "Enchanted Leather",
124 | "ENCHANTED_SPONGE": "Enchanted Sponge",
125 | "ENCHANTED_FEATHER": "Enchanted Feather",
126 | "ENCHANTED_SLIME_BLOCK": "Enchanted Slime Block",
127 | "ENCHANTED_OAK_LOG": "Enchanted Oak Log",
128 | "RABBIT_HIDE": "Rabbit Hide",
129 | "WHITE_GIFT": "White Gift",
130 | "INK_SACK": "Ink Sack",
131 | "FLINT": "Flint",
132 | "ENCHANTED_SPRUCE_LOG": "Enchanted Spruce Log",
133 | "WOLF_TOOTH": "Wolf Tooth",
134 | "ENCHANTED_ROTTEN_FLESH": "Enchanted Rotten Flesh",
135 | "ENCHANTED_GRILLED_PORK": "Enchanted Grilled Pork",
136 | "SULPHUR": "Gunpowder",
137 | "NETHER_STALK": "Nether Wart",
138 | "RABBIT": "Raw Rabbit",
139 | "ENCHANTED_NETHER_STALK": "Enchanted Nether Wart",
140 | "ENCHANTED_REDSTONE_BLOCK": "Enchanted Redstone Block",
141 | "ENCHANTED_QUARTZ_BLOCK": "Enchanted Quartz Block",
142 | "ENCHANTED_CARROT": "Enchanted Carrot",
143 | "ENCHANTED_PUMPKIN": "Enchanted Pumpkin",
144 | "GREEN_CANDY": "Green Candy",
145 | "ENCHANTED_REDSTONE": "Enchanted Redstone",
146 | "ROTTEN_FLESH": "Rotten Flesh",
147 | "ENCHANTED_COOKED_FISH": "Enchanted Cooked Fish",
148 | "OBSIDIAN": "Obsidian",
149 | "ENCHANTED_MAGMA_CREAM": "Enchanted Magma Cream",
150 | "GRAVEL": "Gravel",
151 | "MELON": "Melon",
152 | "ENCHANTED_PACKED_ICE": "Enchanted Packed Ice",
153 | "RAW_FISH:3": "Pufferfish",
154 | "ENCHANTED_PRISMARINE_SHARD": "Enchanted Prismarine Shard",
155 | "ENCHANTED_IRON_BLOCK": "Enchanted Iron Block",
156 | "LEATHER": "Leather",
157 | "ENCHANTED_COOKED_MUTTON": "Enchanted Cooked Mutton",
158 | "BONE": "Bone",
159 | "RAW_FISH:1": "Salmon",
160 | "REVENANT_FLESH": "Revenant Flesh",
161 | "ENCHANTED_PORK": "Enchanted Pork",
162 | "ENCHANTED_GLOWSTONE": "Enchanted Glowstone",
163 | "ENCHANTED_RABBIT": "Enchanted Raw Rabbit",
164 | "ENCHANTED_BREAD": "Enchanted Bread",
165 | "FEATHER": "Feather",
166 | "ENCHANTED_CHARCOAL": "Enchanted Charcoal",
167 | "ENCHANTED_BLAZE_POWDER": "Enchanted Blaze Powder",
168 | "NETHERRACK": "Netherrack",
169 | "SUMMONING_EYE": "Summoning Eye",
170 | "SPONGE": "Sponge",
171 | "BLAZE_ROD": "Blaze Rod",
172 | "ENCHANTED_DARK_OAK_LOG": "Enchanted Dark Oak Log",
173 | "SNOW_BLOCK": "Snow Block",
174 | "ENCHANTED_BAKED_POTATO": "Enchanted Baked Potato",
175 | "COMPACTOR": "Compactor",
176 | "ENCHANTED_DIAMOND": "Enchanted Diamond",
177 | "ENCHANTED_GOLD": "Enchanted Gold",
178 | "WISE_FRAGMENT": "Wise Dragon Fragment",
179 | "PROTECTOR_FRAGMENT": "Protector Dragon Fragment",
180 | "STRONG_FRAGMENT": "Strong Dragon Fragment",
181 | "YOUNG_FRAGMENT": "Young Dragon Fragment",
182 | "UNSTABLE_FRAGMENT": "Unstable Dragon Fragment",
183 | "SUPERIOR_FRAGMENT": "Superior Dragon Fragment",
184 | "OLD_FRAGMENT": "Old Dragon Fragment",
185 | "ENCHANTED_FIREWORK_ROCKET": "Enchanted Firework Rocket",
186 | "ENCHANTED_LAVA_BUCKET": "Enchanted Lava Bucket",
187 | "CATALYST": "Catalyst",
188 | "ENCHANTED_REDSTONE_LAMP": "Enchanted Redstone Lamp",
189 | "ENCHANTED_HAY_BLOCK": "Enchanted Hay Bale",
190 | "HAY_BLOCK": "Hay Bale",
191 | "ENCHANTED_SEEDS": "Enchanted Seeds",
192 | "ENCHANTED_SAND": "Enchanted Sand",
193 | "ENCHANTED_RED_MUSHROOM": "Enchanted Red Mushroom",
194 | "ENCHANTED_BROWN_MUSHROOM": "Enchanted Brown Mushroom",
195 | "ENCHANTED_WET_SPONGE": "Enchanted Wet Sponge",
196 | "HAMSTER_WHEEL": "Hamster Wheel",
197 | "FOUL_FLESH": "Foul Flesh",
198 | "RECOMBOBULATOR_3000": "Recombobulator 3000",
199 | "HOLY_FRAGMENT": "Holy Dragon Fragment",
200 | "FUMING_POTATO_BOOK": "Fuming Potato Book",
201 | "CARROT_BAIT": "Carrot Bait",
202 | "MINNOW_BAIT": "Minnow Bait",
203 | "FISH_BAIT": "Fish Bait",
204 | "LIGHT_BAIT": "Light Bait",
205 | "DARK_BAIT": "Dark Bait",
206 | "SPOOKY_BAIT": "Spooky Bait",
207 | "SPIKED_BAIT": "Spiked Bait",
208 | "BLESSED_BAIT": "Blessed Bait",
209 | "ICE_BAIT": "Ice Bait",
210 | "WHALE_BAIT": "Whale Bait",
211 | "ENCHANTED_CLOWNFISH": "Enchanted Clownfish",
212 | "RAW_FISH:2": "Clownfish",
213 | "ENCHANTED_BONE_BLOCK": "Enchanted Bone Block",
214 | "BOOSTER_COOKIE": "Booster Cookie",
215 | "TIGER_SHARK_TOOTH": "Tiger Shark Tooth",
216 | "BLUE_SHARK_TOOTH": "Blue Shark Tooth",
217 | "NURSE_SHARK_TOOTH": "Nurse Shark Tooth",
218 | "GREAT_WHITE_SHARK_TOOTH": "Great White Shark Tooth",
219 | "SHARK_FIN": "Shark Fin",
220 | "ENCHANTED_SHARK_FIN": "Enchanted Shark Fin",
221 | "BAZAAR_COOKIE": "Bazaar Cookie",
222 | "GRIFFIN_FEATHER": "Griffin Feather",
223 | "DAEDALUS_STICK": "Daedalus Stick",
224 | "ANCIENT_CLAW": "Ancient Claw",
225 | "ENCHANTED_ANCIENT_CLAW": "Enchanted Ancient Claw",
226 | "REFINED_MINERAL": "Refined Mineral",
227 | "SHARK_BAIT": "Shark Bait",
228 | "HYPER_CATALYST": "Hyper Catalyst",
229 | "ECTOPLASM": "Ectoplasm",
230 | "PUMPKIN_GUTS": "Pumpkin Guts",
231 | "SPOOKY_SHARD": "Spooky Shard",
232 | "WEREWOLF_SKIN": "Werewolf Skin",
233 | "SOUL_FRAGMENT": "Soul Fragment",
234 | "JACOBS_TICKET": "Jacob's Ticket",
235 | "EXP_BOTTLE": "Experience Bottle",
236 | "GRAND_EXP_BOTTLE": "Grand Experience Bottle",
237 | "TITANIC_EXP_BOTTLE": "Titanic Experience Bottle",
238 | "COLOSSAL_EXP_BOTTLE": "Colossal Experience Bottle",
239 | "POLISHED_PUMPKIN": "Polished Pumpkin",
240 | "TIGHTLY_TIED_HAY_BALE": "Tightly-Tied Hay Bale",
241 | "MUTANT_NETHER_STALK": "Mutant Nether Wart",
242 | "REFINED_MITHRIL": "Refined Mithril",
243 | "TITANIUM_ORE": "Titanium",
244 | "MITHRIL_ORE": "Mithril",
245 | "STARFALL": "Starfall",
246 | "REFINED_TITANIUM": "Refined Titanium",
247 | "REFINED_DIAMOND": "Refined Diamond",
248 | "JERRY_BOX_GOLDEN": "Golden Jerry Box",
249 | "JERRY_BOX_PURPLE": "Purple Jerry Box",
250 | "JERRY_BOX_BLUE": "Blue Jerry Box",
251 | "JERRY_BOX_GREEN": "Green Jerry Box",
252 | "ENCHANTED_MITHRIL": "Enchanted Mithril",
253 | "ENCHANTED_TITANIUM": "Enchanted Titanium",
254 | "MAGMA_BUCKET": "Magma Bucket",
255 | "PLASMA_BUCKET": "Plasma Bucket",
256 | "DWARVEN_COMPACTOR": "Dwarven Super Compactor",
257 | "POWER_CRYSTAL": "Power Crystal",
258 | "TREASURITE": "Treasurite",
259 | "ENCHANTED_NETHERRACK": "Enchanted Netherrack",
260 | "NULL_SPHERE": "Null Sphere",
261 | "NULL_OVOID": "Null Ovoid",
262 | "NULL_ATOM": "Null Atom",
263 | "RAW_SOULFLOW": "Raw Soulflow",
264 | "SOULFLOW": "Soulflow",
265 | "ABSOLUTE_ENDER_PEARL": "Absolute Ender Pearl",
266 | "ROUGH_JADE_GEM": "\u2618 Rough Jade Gemstone",
267 | "FLAWED_JADE_GEM": "\u2618 Flawed Jade Gemstone",
268 | "FINE_JADE_GEM": "\u2618 Fine Jade Gemstone",
269 | "FLAWLESS_JADE_GEM": "\u2618 Flawless Jade Gemstone",
270 | "PERFECT_JADE_GEM": "\u2618 Perfect Jade Gemstone",
271 | "ROUGH_AMBER_GEM": "\u2E15 Rough Amber Gemstone",
272 | "FLAWED_AMBER_GEM": "\u2E15 Flawed Amber Gemstone",
273 | "FINE_AMBER_GEM": "\u2E15 Fine Amber Gemstone",
274 | "FLAWLESS_AMBER_GEM": "\u2E15 Flawless Amber Gemstone",
275 | "PERFECT_AMBER_GEM": "\u2E15 Perfect Amber Gemstone",
276 | "ROUGH_TOPAZ_GEM": "\u2727 Rough Topaz Gemstone",
277 | "FLAWED_TOPAZ_GEM": "\u2727 Flawed Topaz Gemstone",
278 | "FINE_TOPAZ_GEM": "\u2727 Fine Topaz Gemstone",
279 | "FLAWLESS_TOPAZ_GEM": "\u2727 Flawless Topaz Gemstone",
280 | "PERFECT_TOPAZ_GEM": "\u2727 Perfect Topaz Gemstone",
281 | "ROUGH_SAPPHIRE_GEM": "\u270E Rough Sapphire Gemstone",
282 | "FLAWED_SAPPHIRE_GEM": "\u270E Flawed Sapphire Gemstone",
283 | "FINE_SAPPHIRE_GEM": "\u270E Fine Sapphire Gemstone",
284 | "FLAWLESS_SAPPHIRE_GEM": "\u270E Flawless Sapphire Gemstone",
285 | "PERFECT_SAPPHIRE_GEM": "\u270E Perfect Sapphire Gemstone",
286 | "ROUGH_AMETHYST_GEM": "\u2748 Rough Amethyst Gemstone",
287 | "FLAWED_AMETHYST_GEM": "\u2748 Flawed Amethyst Gemstone",
288 | "FINE_AMETHYST_GEM": "\u2748 Fine Amethyst Gemstone",
289 | "FLAWLESS_AMETHYST_GEM": "\u2748 Flawless Amethyst Gemstone",
290 | "PERFECT_AMETHYST_GEM": "\u2748 Perfect Amethyst Gemstone",
291 | "ROUGH_JASPER_GEM": "\u2741 Rough Jasper Gemstone",
292 | "FLAWED_JASPER_GEM": "\u2741 Flawed Jasper Gemstone",
293 | "FINE_JASPER_GEM": "\u2741 Fine Jasper Gemstone",
294 | "FLAWLESS_JASPER_GEM": "\u2741 Flawless Jasper Gemstone",
295 | "PERFECT_JASPER_GEM": "\u2741 Perfect Jasper Gemstone",
296 | "ROUGH_RUBY_GEM": "\u2764 Rough Ruby Gemstone",
297 | "FLAWED_RUBY_GEM": "\u2764 Flawed Ruby Gemstone",
298 | "FINE_RUBY_GEM": "\u2764 Fine Ruby Gemstone",
299 | "FLAWLESS_RUBY_GEM": "\u2764 Flawless Ruby Gemstone",
300 | "PERFECT_RUBY_GEM": "\u2764 Perfect Ruby Gemstone",
301 | "ROUGH_OPAL_GEM": "\u2742 Rough Opal Gemstone",
302 | "FLAWED_OPAL_GEM": "\u2742 Flawed Opal Gemstone",
303 | "FINE_OPAL_GEM": "\u2742 Fine Opal Gemstone",
304 | "FLAWLESS_OPAL_GEM": "\u2742 Flawless Opal Gemstone",
305 | "PERFECT_OPAL_GEM": "\u2742 Perfect Opal Gemstone",
306 | "HARD_STONE": "Hard Stone",
307 | "ENCHANTED_HARD_STONE": "Enchanted Hard Stone",
308 | "CONCENTRATED_STONE": "Concentrated Stone",
309 | "YOGGIE": "Yoggie",
310 | "SLUDGE_JUICE": "Sludge Juice",
311 | "WORM_MEMBRANE": "Worm Membrane",
312 | "GOBLIN_EGG_GREEN": "Green Goblin Egg",
313 | "GOBLIN_EGG_BLUE": "Blue Goblin Egg",
314 | "GOBLIN_EGG_RED": "Red Goblin Egg",
315 | "GOBLIN_EGG_YELLOW": "Yellow Goblin Egg",
316 | "GOBLIN_EGG": "Goblin Egg",
317 | "ARACHNE_KEEPER_FRAGMENT": "Arachne's Keeper Fragment",
318 | "SOUL_STRING": "Soul String",
319 | "CHEESE_FUEL": "Tasty Cheese",
320 | "MYCEL": "Mycelium",
321 | "ENCHANTED_MYCELIUM": "Enchanted Mycelium",
322 | "ENCHANTED_MYCELIUM_CUBE": "Enchanted Mycelium Cube",
323 | "SULPHUR_ORE": "Sulphur",
324 | "ENCHANTED_SULPHUR": "Enchanted Sulphur",
325 | "ENCHANTED_SULPHUR_CUBE": "Enchanted Sulphur Cube",
326 | "SAND:1": "Red Sand",
327 | "ENCHANTED_RED_SAND": "Enchanted Red Sand",
328 | "ENCHANTED_RED_SAND_CUBE": "Enchanted Red Sand Cube",
329 | "CORRUPTED_BAIT": "Corrupted Bait",
330 | "HORNS_OF_TORMENT": "Horns Of Torment",
331 | "DARK_ORB": "Dark Orb",
332 | "ROCK_GEMSTONE": "Rock Gemstone",
333 | "END_STONE_SHULKER": "End Stone Shulker",
334 | "ENDER_MONOCLE": "Ender Monocle",
335 | "HAZMAT_ENDERMAN": "Hazmat Enderman",
336 | "LUXURIOUS_SPOOL": "Luxurious Spool",
337 | "KUUDRA_MANDIBLE": "Kuudra Mandible",
338 | "BLAZE_ROD_DISTILLATE": "Blaze Rod Distillate",
339 | "ACACIA_BIRDHOUSE": "Acacia Birdhouse",
340 | "DERELICT_ASHE": "Derelict Ashe",
341 | "MOIL_LOG": "Moil Log",
342 | "MAGMA_FISH_GOLD": "Gold Magmafish",
343 | "MELON_BLOCK": "Melon Block",
344 | "AMBER_MATERIAL": "Amber Material",
345 | "CRUDE_GABAGOOL_DISTILLATE": "Gabagool Distillate",
346 | "METEOR_SHARD": "Meteor Shard",
347 | "DIAMOND_ATOM": "Diamond Atom",
348 | "PRECURSOR_GEAR": "Precursor Gear",
349 | "NECROMANCER_BROOCH": "Necromancer Brooch",
350 | "DEEP_SEA_ORB": "Deep Sea Orb",
351 | "PETRIFIED_STARFALL": "Petrified Starfall",
352 | "RED_SCARF": "Red Scarf",
353 | "SADAN_BROOCH": "Sadan's Brooch",
354 | "ONYX": "Onyx",
355 | "OMEGA_EGG": "Omega Enchanted Egg",
356 | "MAGMA_CREAM_DISTILLATE": "Magma Cream Distillate",
357 | "FURBALL": "Furball",
358 | "GIANT_TOOTH": "Giant Tooth",
359 | "ENDSTONE_GEODE": "End Stone Geode",
360 | "OPTICAL_LENS": "Optical Lens",
361 | "HOT_BAIT": "Hot Bait",
362 | "BEATING_HEART": "Beating Heart",
363 | "AMALGAMATED_CRIMSONITE": "Amalgamated Crimsonite",
364 | "TOIL_LOG": "Toil Log",
365 | "SALMON_OPAL": "Salmon Opal",
366 | "AOTE_STONE": "Warped Stone",
367 | "GOLDEN_BALL": "Golden Ball",
368 | "MAGMA_FISH": "Magmafish",
369 | "GLOWSTONE_DUST_DISTILLATE": "Glowstone Distillate",
370 | "MANDRAA": "Mandraa",
371 | "MOLTEN_CUBE": "Molten Cube",
372 | "BULKY_STONE": "Bulky Stone",
373 | "INFERNO_VERTEX": "Inferno Vertex",
374 | "REFINED_AMBER": "Refined Amber",
375 | "JADERALD": "Jaderald",
376 | "NETHER_STALK_DISTILLATE": "Nether Wart Distillate",
377 | "RARE_DIAMOND": "Rare Diamond",
378 | "CRUDE_GABAGOOL": "Crude Gabagool",
379 | "DRAGON_SCALE": "Dragon Scale",
380 | "DRAGON_HORN": "Dragon Horn",
381 | "HARDENED_WOOD": "Hardened Wood",
382 | "HEAVY_GABAGOOL": "Heavy Gabagool",
383 | "INFERNO_APEX": "Inferno Apex",
384 | "ECCENTRIC_PAINTING": "Eccentric Painting",
385 | "REAPER_PEPPER": "Reaper Pepper",
386 | "DIAMONITE": "Diamonite",
387 | "SPIRIT_DECOY": "Spirit Stone",
388 | "HYPERGOLIC_GABAGOOL": "Hypergolic Gabagool",
389 | "OBSIDIAN_TABLET": "Obsidian Tablet",
390 | "VITAMIN_DEATH": "Vitamin Death",
391 | "CANDY_CORN": "Candy Corn",
392 | "MIDAS_JEWEL": "Midas Jewel",
393 | "PREMIUM_FLESH": "Premium Flesh",
394 | "PURE_MITHRIL": "Pure Mithril",
395 | "JERRY_STONE": "Jerry Stone",
396 | "MOLTEN_POWDER": "Molten Powder",
397 | "ROCK_CANDY": "Rock Candy",
398 | "MAGMA_URCHIN": "Magma Urchin",
399 | "SEARING_STONE": "Searing Stone",
400 | "BLESSED_FRUIT": "Blessed Fruit",
401 | "HOT_STUFF": "Hot Stuff",
402 | "SUSPICIOUS_VIAL": "Suspicious Vial",
403 | "DIRT_BOTTLE": "Dirt Bottle",
404 | "RED_NOSE": "Red Nose",
405 | "MAGMA_FISH_SILVER": "Silver Magmafish",
406 | "DRAGON_CLAW": "Dragon Claw",
407 | "LUCKY_DICE": "Lucky Dice",
408 | "FUEL_GABAGOOL": "Fuel Gabagool",
409 | "PRECIOUS_PEARL": "Precious Pearl",
410 | "SCORCHED_BOOKS": "Scorched Books",
411 | "MAGMA_FISH_DIAMOND": "Diamond Magmafish",
412 | "WITHER_BLOOD": "Wither Blood",
413 | "BLAZE_WAX": "Blaze Wax",
414 | "LAPIS_CRYSTAL": "Lapis Crystal",
415 | "SHINY_PRISM": "Shiny Prism"
416 | }
417 |
--------------------------------------------------------------------------------
/discord-bot/src/bazaar/performance.js:
--------------------------------------------------------------------------------
1 | // Version 2.0
2 |
3 | module.exports = bazaarData => {
4 | bazaarData.forEach(product => {
5 | let diff = product.sellOrderPrice * 0.99 - product.buyOrderPrice;
6 | product.profitFlowPerMinute = ((product.sellCount + product.buyCount) === 0)
7 | ? 0 : (product.sellCount * product.buyCount) / (10080
8 | * (product.sellCount + product.buyCount)) * diff;
9 | });
10 |
11 | bazaarData.sort((a, b) => b.profitFlowPerMinute - a.profitFlowPerMinute);
12 | return bazaarData;
13 | }
14 |
15 | /*
16 |
17 | VERSION 1.0
18 |
19 | module.exports = bazaarData => {
20 | bazaarData.sort((a, b) => {
21 | if (!a.buy || !a.sell) {
22 | return 1;
23 | } else if (!b.buy || !b.sell) {
24 | return -1;
25 | }
26 |
27 | return (
28 | b.sell.pricePerUnit * 0.99 -
29 | b.buy.pricePerUnit -
30 | (a.sell.pricePerUnit * 0.99 - a.buy.pricePerUnit)
31 | );
32 | });
33 | let i = 1;
34 | bazaarData.forEach(data => {
35 | data.rankings = {
36 | diff: 176 - i++
37 | };
38 | });
39 | i = 1;
40 |
41 | bazaarData.sort((a, b) => {
42 | if (!a.buy || !a.sell) {
43 | return 1;
44 | } else if (!b.buy || !b.sell) {
45 | return -1;
46 | }
47 |
48 | return (
49 | (b.sell.pricePerUnit * 0.99) / b.buy.pricePerUnit -
50 | (a.sell.pricePerUnit * 0.99) / a.buy.pricePerUnit
51 | );
52 | });
53 |
54 | bazaarData.forEach(data => {
55 | data.rankings.change = 176 - i++;
56 | });
57 | i = 1;
58 |
59 | bazaarData.sort((a, b) => {
60 | return ((b.buyCount + b.sellCount) / 2 - (a.buyCount + a.sellCount) / 2);
61 | });
62 |
63 | bazaarData.forEach(data => {
64 | data.rankings.instants = 176 - i++;
65 | });
66 | i = 1;
67 |
68 | return bazaarData;
69 | };
70 | */
--------------------------------------------------------------------------------
/discord-bot/src/index.js:
--------------------------------------------------------------------------------
1 | const {Client} = require("discord.js");
2 | const client = new Client({intents: 32767});
3 | require('dotenv').config()
4 |
5 | const prefix = "~";
6 | const channel = process.env.CHANNEL;
7 | client.on("ready", () => {
8 | const {cacheUpdate} = require("./bazaar/bazaar.js");
9 | cacheUpdate();
10 | console.log("Login successful!")
11 | });
12 |
13 | client.login(process.env.BOT);
14 |
15 | module.exports = {client, prefix, channel};
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | loom.platform=forge
2 | org.gradle.jvmargs=-Xmx2g
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/symt/BazaarNotifier/b4b089d210dac724b50f494cb781427f2366052b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | mavenCentral()
4 | gradlePluginPortal()
5 | maven("https://oss.sonatype.org/content/repositories/snapshots")
6 | maven("https://maven.architectury.dev/")
7 | maven("https://maven.fabricmc.net")
8 | maven("https://maven.minecraftforge.net/")
9 | maven("https://repo.spongepowered.org/maven/")
10 | maven("https://repo.sk1er.club/repository/maven-releases/")
11 | }
12 | resolutionStrategy {
13 | eachPlugin {
14 | when (requested.id.id) {
15 | "gg.essential.loom" -> useModule("gg.essential:architectury-loom:${requested.version}")
16 | }
17 | }
18 | }
19 | }
20 |
21 | rootProject.name = "BazaarNotifier"
22 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/BazaarNotifier.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn;
2 |
3 | import com.google.common.collect.BiMap;
4 | import com.google.common.collect.HashBiMap;
5 | import com.google.gson.Gson;
6 | import com.google.gson.JsonObject;
7 | import com.google.gson.JsonSyntaxException;
8 | import com.google.gson.stream.JsonReader;
9 | import dev.meyi.bn.commands.BazaarNotifierCommand;
10 | import dev.meyi.bn.config.Configuration;
11 | import dev.meyi.bn.handlers.ChestTickHandler;
12 | import dev.meyi.bn.handlers.EventHandler;
13 | import dev.meyi.bn.handlers.MouseHandler;
14 | import dev.meyi.bn.handlers.UpdateHandler;
15 | import dev.meyi.bn.json.Order;
16 | import dev.meyi.bn.json.resp.BazaarResponse;
17 | import dev.meyi.bn.modules.Module;
18 | import dev.meyi.bn.modules.ModuleList;
19 | import dev.meyi.bn.utilities.ReflectionHelper;
20 | import dev.meyi.bn.utilities.ScheduledEvents;
21 | import dev.meyi.bn.utilities.Utils;
22 | import java.io.File;
23 | import java.io.IOException;
24 | import java.io.InputStreamReader;
25 | import java.io.StringReader;
26 | import java.nio.charset.StandardCharsets;
27 | import java.nio.file.Files;
28 | import java.nio.file.Paths;
29 | import java.security.KeyManagementException;
30 | import java.security.NoSuchAlgorithmException;
31 | import java.text.DecimalFormat;
32 | import java.util.ArrayList;
33 | import java.util.Objects;
34 | import net.minecraft.util.EnumChatFormatting;
35 | import net.minecraftforge.client.ClientCommandHandler;
36 | import net.minecraftforge.common.MinecraftForge;
37 | import net.minecraftforge.fml.common.Mod;
38 | import net.minecraftforge.fml.common.event.FMLInitializationEvent;
39 | import net.minecraftforge.fml.common.event.FMLLoadCompleteEvent;
40 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
41 |
42 |
43 | @Mod(modid = BazaarNotifier.MODID, version = BazaarNotifier.VERSION)
44 | public class BazaarNotifier {
45 |
46 | public static final String MODID = "BazaarNotifier";
47 | public static final String VERSION = "1.7.5";
48 | public static final String prefix =
49 | EnumChatFormatting.GOLD + "[" + EnumChatFormatting.YELLOW + "BN" + EnumChatFormatting.GOLD
50 | + "] " + EnumChatFormatting.RESET;
51 | public static final String header =
52 | EnumChatFormatting.GOLD + "" + EnumChatFormatting.BOLD + "" + EnumChatFormatting.STRIKETHROUGH
53 | + "-------" + EnumChatFormatting.RESET + "" + EnumChatFormatting.GOLD + ""
54 | + EnumChatFormatting.BOLD + " [ " + EnumChatFormatting.YELLOW + "BazaarNotifier"
55 | + EnumChatFormatting.GOLD + "" + EnumChatFormatting.BOLD + " ] "
56 | + EnumChatFormatting.STRIKETHROUGH + "-------";
57 | public static final String RESOURCE_LOCATION = "https://raw.githubusercontent.com/symt/BazaarNotifier/resources/resources.json";
58 | public static DecimalFormat df = new DecimalFormat("#,##0.0");
59 | public static DecimalFormat dfNoDecimal = new DecimalFormat("#,###");
60 |
61 | public static boolean activeBazaar = true;
62 | public static boolean inBazaar = false;
63 | public static boolean forceRender = false;
64 |
65 |
66 | public static ArrayList orders = new ArrayList<>();
67 | public static BazaarResponse bazaarDataRaw;
68 | public static ModuleList modules;
69 | public static Configuration config;
70 | public static JsonObject resources;
71 |
72 | public static String guiToOpen = "";
73 |
74 | public static JsonObject enchantCraftingList;
75 | public static BiMap bazaarConv = HashBiMap.create();
76 |
77 | public static File resourcesFile;
78 |
79 | public static void resetMod() {
80 | modules.resetAll();
81 | orders = new ArrayList<>();
82 | }
83 |
84 | public static void resetScale() {
85 | modules.resetScale();
86 | }
87 |
88 | @Mod.EventHandler
89 | public void preInit(FMLPreInitializationEvent event) {
90 | ReflectionHelper.setup();
91 | File bnDir = new File(event.getModConfigurationDirectory(), "BazaarNotifier");
92 | //noinspection ResultOfMethodCallIgnored
93 | bnDir.mkdirs();
94 | resourcesFile = new File(bnDir, "resources.json");
95 | Gson gson = new Gson();
96 |
97 | try {
98 | if (resourcesFile.isFile()) {
99 | try {
100 | byte[] bytes = Files.readAllBytes(Paths.get(resourcesFile.getPath()));
101 | if (bytes.length == 0) throw new JsonSyntaxException("Invalid JSON in Resources File");
102 | JsonReader resourcesReader = new JsonReader(new StringReader(new String(
103 | bytes, StandardCharsets.UTF_8)));
104 | resourcesReader.setLenient(true);
105 | resources = gson.fromJson(resourcesReader, JsonObject.class);
106 | } catch (JsonSyntaxException | ClassCastException e) {
107 | e.printStackTrace();
108 | JsonReader reader = new JsonReader(new InputStreamReader(Objects.requireNonNull(
109 | BazaarNotifier.class.getResourceAsStream("/resources.json")), StandardCharsets.UTF_8));
110 | reader.setLenient(true);
111 | resources = gson.fromJson(reader, JsonObject.class);
112 | }
113 | } else {
114 | JsonReader reader = new JsonReader(new InputStreamReader(Objects.requireNonNull(
115 | BazaarNotifier.class.getResourceAsStream("/resources.json")), StandardCharsets.UTF_8));
116 | reader.setLenient(true);
117 | resources = gson.fromJson(reader, JsonObject.class);
118 | }
119 | } catch (IOException e) {
120 | e.printStackTrace();
121 | }
122 |
123 | try {
124 | Utils.updateResources();
125 | } catch (IOException | KeyManagementException | NoSuchAlgorithmException | ClassCastException e) {
126 | System.err.println("Error while getting resources from GitHub");
127 | e.printStackTrace();
128 | JsonObject bazaarConversions = resources.getAsJsonObject("bazaarConversions");
129 | enchantCraftingList = resources.getAsJsonObject("enchantCraftingList");
130 | bazaarConv = Utils.jsonToBimap(bazaarConversions);
131 | }
132 | }
133 |
134 |
135 | @Mod.EventHandler
136 | public void init(FMLInitializationEvent event) {
137 | MinecraftForge.EVENT_BUS.register(new EventHandler());
138 | MinecraftForge.EVENT_BUS.register(new ChestTickHandler());
139 | MinecraftForge.EVENT_BUS.register(new MouseHandler());
140 | MinecraftForge.EVENT_BUS.register(new UpdateHandler());
141 | ClientCommandHandler.instance.registerCommand(new BazaarNotifierCommand());
142 |
143 |
144 |
145 | config = new Configuration();
146 |
147 |
148 | Runtime.getRuntime()
149 | .addShutdownHook(
150 | new Thread(
151 | () -> {
152 | Utils.saveResources(resourcesFile, resources);
153 | //config.save();
154 | }));
155 |
156 | }
157 | @Mod.EventHandler
158 | public void done(FMLLoadCompleteEvent e){
159 | ScheduledEvents.create();
160 | modules = new ModuleList();
161 | if (BazaarNotifier.config.firstLoad){
162 | for(Module m : modules){
163 | m.setActive(true);
164 | m.position.setPosition(20, 20);
165 | m.showInChat = false;
166 | m.showInGuis = false;
167 | }
168 | BazaarNotifier.config.firstLoad = false;
169 | }
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/commands/BazaarNotifierCommand.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.commands;
2 |
3 | import dev.meyi.bn.BazaarNotifier;
4 | import dev.meyi.bn.json.resp.BazaarItem;
5 | import dev.meyi.bn.modules.calc.BankCalculator;
6 | import dev.meyi.bn.modules.calc.CraftingCalculator;
7 | import dev.meyi.bn.modules.calc.SuggestionCalculator;
8 | import dev.meyi.bn.utilities.Utils;
9 | import java.io.IOException;
10 | import java.security.KeyManagementException;
11 | import java.security.NoSuchAlgorithmException;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import net.minecraft.command.CommandBase;
15 | import net.minecraft.command.ICommandSender;
16 | import net.minecraft.entity.player.EntityPlayer;
17 | import net.minecraft.event.ClickEvent;
18 | import net.minecraft.event.ClickEvent.Action;
19 | import net.minecraft.util.BlockPos;
20 | import net.minecraft.util.ChatComponentText;
21 | import net.minecraft.util.EnumChatFormatting;
22 | import org.apache.commons.lang3.text.WordUtils;
23 |
24 |
25 | public class BazaarNotifierCommand extends CommandBase {
26 |
27 | private static long date = 0; // This can be set to System.currentTimeMillis, but it makes testing annoying
28 |
29 | @Override
30 | public List getCommandAliases() {
31 | return new ArrayList() {
32 | {
33 | add("bn");
34 | }
35 | };
36 | }
37 |
38 |
39 | @Override
40 | public String getCommandName() {
41 | return "bazaarnotifier";
42 | }
43 |
44 | @Override
45 | public String getCommandUsage(ICommandSender sender) {
46 | return "/bazaarnotifier [subcommand]";
47 | }
48 |
49 | @Override
50 | public void processCommand(ICommandSender ics, String[] args) {
51 | if (ics instanceof EntityPlayer) {
52 | EntityPlayer player = (EntityPlayer) ics;
53 | if (args.length > 0 && args[0].equalsIgnoreCase("reset")) {
54 | if (args.length == 1 || args[1].equalsIgnoreCase("all")) {
55 | BazaarNotifier.resetMod();
56 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
57 | + "All module locations have been reset and the order list has been emptied."));
58 | } else if (args[1].equalsIgnoreCase("orders") && args.length == 2) {
59 | BazaarNotifier.orders.clear();
60 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
61 | + "Your orders have been cleared."));
62 | } else if (args[1].equalsIgnoreCase("scale") && args.length == 2) {
63 | BazaarNotifier.resetScale();
64 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
65 | + "Your scale for every module has been reset."));
66 | } else if (args[1].equalsIgnoreCase("bank") && args.length == 2) {
67 | BankCalculator.reset();
68 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
69 | + "Your bank module has been reset."));
70 | } else {
71 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
72 | + "That module doesn't exist."));
73 | }
74 | } else if (args.length >= 1 && args[0].equalsIgnoreCase("find")) {
75 | if (args.length == 1) {
76 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
77 | + "Use the following format: /bn find (item)"));
78 | } else {
79 | String itemName = WordUtils
80 | .capitalize(String.join(" ", args).substring(5).replaceAll("-", " "));
81 | if (BazaarNotifier.bazaarDataRaw != null) {
82 | String[] itemSet = Utils.getItemIdFromName(itemName);
83 | itemName = itemSet[0];
84 | String itemConv = itemSet[1];
85 | BazaarItem item = BazaarNotifier.bazaarDataRaw.products.get(itemConv);
86 | String findItemString = BazaarNotifier.prefix + EnumChatFormatting.RED
87 | + "Please provide a valid item to find.";
88 |
89 | String bulletPoint = EnumChatFormatting.WHITE + "\u2022 ";
90 | String separator = EnumChatFormatting.RED + " / ";
91 |
92 | if (BazaarNotifier.bazaarConv.containsKey(itemConv)) {
93 | findItemString = BazaarNotifier.header + "\n" +
94 | EnumChatFormatting.DARK_RED + EnumChatFormatting.BOLD + WordUtils
95 | .capitalize(itemName) + "\n " + bulletPoint +
96 | EnumChatFormatting.RED + "Buy Order: " +
97 | EnumChatFormatting.GRAY + BazaarNotifier.df.format(item.sell_summary.size() == 0 ?
98 | 0 : item.sell_summary.get(0).pricePerUnit) + "\n " + bulletPoint +
99 | EnumChatFormatting.RED + "Sell Offer: " +
100 | EnumChatFormatting.GRAY + BazaarNotifier.df.format(item.buy_summary.size() == 0 ?
101 | 0 : item.buy_summary.get(0).pricePerUnit) + "\n " + bulletPoint +
102 | EnumChatFormatting.RED + "Estimated Profit: " +
103 | EnumChatFormatting.GRAY + BazaarNotifier.df
104 | .format(SuggestionCalculator.calculateEP(item)) + "\n";
105 | }
106 |
107 | if (BazaarNotifier.enchantCraftingList.getAsJsonObject("other").has(itemConv)) {
108 |
109 | String[] prices = CraftingCalculator.getEnchantCraft(itemConv);
110 |
111 | findItemString +=
112 | EnumChatFormatting.DARK_RED + "" + EnumChatFormatting.BOLD + "Crafting (" +
113 | EnumChatFormatting.GRAY
114 | + "Buy order" + separator + EnumChatFormatting.GRAY + "Instant buy"
115 | + EnumChatFormatting.DARK_RED + EnumChatFormatting.BOLD + ")" + "\n " +
116 | bulletPoint +
117 | EnumChatFormatting.RED + "Profit (Instant Sell): " +
118 | EnumChatFormatting.GRAY + BazaarNotifier.df
119 | .format(Double.parseDouble(prices[0])) + separator + EnumChatFormatting.GRAY +
120 | BazaarNotifier.df.format(Double.parseDouble(prices[3])) + "\n " + bulletPoint
121 | +
122 | EnumChatFormatting.RED + "Profit (Sell Offer): " +
123 | EnumChatFormatting.GRAY + BazaarNotifier.df
124 | .format(Double.parseDouble(prices[1])) + separator + EnumChatFormatting.GRAY +
125 | BazaarNotifier.df.format(Double.parseDouble(prices[4])) + "\n " + bulletPoint
126 | +
127 | EnumChatFormatting.RED + "Profit per 1M: " +
128 | EnumChatFormatting.GRAY + BazaarNotifier.df
129 | .format(Double.parseDouble(prices[2])) + separator + EnumChatFormatting.GRAY +
130 | BazaarNotifier.df.format(Double.parseDouble(prices[5])) + "\n" +
131 | BazaarNotifier.header;
132 |
133 | } else if (BazaarNotifier.bazaarConv.containsKey(itemConv)) {
134 | findItemString += BazaarNotifier.header;
135 | }
136 |
137 | player.addChatMessage(new ChatComponentText(findItemString));
138 |
139 | } else {
140 | player.addChatMessage(new ChatComponentText(
141 | BazaarNotifier.prefix + EnumChatFormatting.RED
142 | + "Please wait a moment for the mod to get bazaar information"));
143 | }
144 | }
145 | } else if (args.length == 1 && args[0].equalsIgnoreCase("help")) {
146 | player.addChatMessage(new ChatComponentText(
147 | BazaarNotifier.header + "\n"
148 | + EnumChatFormatting.RED + "/bn " + EnumChatFormatting.DARK_RED + "\u2192"
149 | + EnumChatFormatting.GRAY + " Opens the GUI\n"
150 | + EnumChatFormatting.RED + "/bn reset (value) " + EnumChatFormatting.DARK_RED
151 | + "\u2192" + EnumChatFormatting.GRAY
152 | + " Reset specific modules to default settings\n"
153 | + EnumChatFormatting.RED + "/bn api (key) " + EnumChatFormatting.DARK_RED + "\u2192"
154 | + EnumChatFormatting.GRAY + " Sets your api key for crafting module\n"
155 | + EnumChatFormatting.RED + "/bn find (item) " + EnumChatFormatting.DARK_RED
156 | + "\u2192" + EnumChatFormatting.GRAY + " Search specific item's prices and EP\n"
157 | + EnumChatFormatting.RED + "/bn discord " + EnumChatFormatting.DARK_RED + "\u2192"
158 | + EnumChatFormatting.GRAY + " Provides discord link (beta access + more)\n"
159 | + BazaarNotifier.header
160 | ));
161 | } else if (args.length == 1 && args[0].equalsIgnoreCase("__force")) {
162 | BazaarNotifier.forceRender ^= true;
163 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
164 | + "This command is intended for testing purposes only, use it at your own peril. Forced rendering has been turned "
165 | + EnumChatFormatting.DARK_RED + (BazaarNotifier.forceRender ? "on" : "off")));
166 | } else if (args.length == 1 && args[0].equalsIgnoreCase("discord")) {
167 | ChatComponentText discordLink = new ChatComponentText(
168 | EnumChatFormatting.DARK_GREEN + "" + EnumChatFormatting.BOLD
169 | + "[DISCORD LINK]");
170 | discordLink
171 | .setChatStyle(discordLink.getChatStyle().setChatClickEvent(new ClickEvent(
172 | Action.OPEN_URL,
173 | "https://discord.com/invite/wjpJSVSwvD")));
174 | ChatComponentText supportLink = new ChatComponentText(
175 | EnumChatFormatting.DARK_GREEN + "" + EnumChatFormatting.BOLD
176 | + "[PATREON LINK]");
177 | supportLink
178 | .setChatStyle(supportLink.getChatStyle().setChatClickEvent(new ClickEvent(
179 | Action.OPEN_URL,
180 | "https://patreon.com/meyi")));
181 |
182 | player.addChatMessage(new ChatComponentText(
183 | BazaarNotifier.prefix + "\n" + EnumChatFormatting.GREEN + "Join the discord server: ")
184 | .appendSibling(discordLink).appendSibling(
185 | new ChatComponentText(
186 | "\n" + EnumChatFormatting.GREEN + "If you want, you can support my work: ")
187 | .appendSibling(supportLink))
188 | .appendSibling(new ChatComponentText("\n" + BazaarNotifier.prefix)));
189 |
190 | } else if (args.length == 1 && args[0].equalsIgnoreCase("update")) {
191 | if (date < System.currentTimeMillis() - (10 * 60 * 1000)) {
192 | new Thread(() -> {
193 | try {
194 | Utils.updateResources();
195 | date = System.currentTimeMillis();
196 | } catch (IOException | KeyManagementException | NoSuchAlgorithmException | ClassCastException e) {
197 | player.addChatMessage(new ChatComponentText(
198 | BazaarNotifier.prefix + EnumChatFormatting.RED
199 | + "Resource update failed. Please try again."));
200 | }
201 | }).start();
202 | player.addChatMessage(
203 | new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.GREEN
204 | + "Updating required resources from GitHub"));
205 |
206 | } else {
207 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
208 | + "Please wait 10 minutes before running that command again"));
209 | }
210 | } else if (args.length > 0) {
211 | player.addChatMessage(new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
212 | + "The command you just tried to do doesn't exist. Do /bn"));
213 | } else {
214 | BazaarNotifier.guiToOpen = "settings";
215 | }
216 | }
217 | }
218 |
219 | public boolean canCommandSenderUseCommand(final ICommandSender sender) {
220 | return true;
221 | }
222 |
223 |
224 | @Override
225 | public List addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) {
226 | List arguments = new ArrayList<>();
227 |
228 | if (args.length <= 1) {
229 | new ArrayList() {
230 | {
231 | add("discord");
232 | add("find");
233 | add("reset");
234 | add("update");
235 | }
236 | }.forEach(cmd -> {
237 | if (args[0].trim().length() == 0 || cmd.startsWith(args[0].toLowerCase())) {
238 | arguments.add(cmd);
239 | }
240 | });
241 | } else {
242 | if (args.length <= 2 && args[0].equalsIgnoreCase("reset")) {
243 | new ArrayList() {
244 | {
245 | add("all");
246 | add("orders");
247 | add("scale");
248 | add("bank");
249 | }
250 | }.forEach(cmd -> {
251 | if (args[1].trim().length() == 0 || cmd.startsWith(args[1].toLowerCase())) {
252 | arguments.add(cmd);
253 | }
254 | });
255 | } else if (args.length <= 2 && args[0].equalsIgnoreCase("find")) {
256 | ArrayList a = new ArrayList<>();
257 | for (String s : BazaarNotifier.bazaarConv.values()) {
258 | s = s.replace(' ', '-');
259 | a.add(s.toLowerCase());
260 | }
261 | a.forEach(cmd -> {
262 | if (args[1].trim().length() == 0 || cmd.startsWith(args[1].toLowerCase())) {
263 | arguments.add(cmd);
264 | }
265 | if (!arguments.contains(cmd) && (cmd.contains(args[1].toLowerCase()))) {
266 | arguments.add(cmd);
267 | }
268 | });
269 | }
270 | }
271 | return arguments;
272 | }
273 | }
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/config/Configuration.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.config;
2 |
3 | import cc.polyfrost.oneconfig.config.Config;
4 | import cc.polyfrost.oneconfig.config.annotations.Button;
5 | import cc.polyfrost.oneconfig.config.annotations.HUD;
6 | import cc.polyfrost.oneconfig.config.annotations.Switch;
7 | import cc.polyfrost.oneconfig.config.core.OneColor;
8 | import cc.polyfrost.oneconfig.config.data.Mod;
9 | import cc.polyfrost.oneconfig.config.data.ModType;
10 | import cc.polyfrost.oneconfig.config.migration.JsonMigrator;
11 | import cc.polyfrost.oneconfig.config.migration.JsonName;
12 | import dev.meyi.bn.BazaarNotifier;
13 | import dev.meyi.bn.modules.calc.BankCalculator;
14 | import dev.meyi.bn.modules.module.BankModule;
15 | import dev.meyi.bn.modules.module.CraftingModule;
16 | import dev.meyi.bn.modules.module.NotificationModule;
17 | import dev.meyi.bn.modules.module.SuggestionModule;
18 | import dev.meyi.bn.utilities.Defaults;
19 |
20 | import java.awt.Color;
21 |
22 |
23 | public class Configuration extends Config {
24 |
25 | public Configuration() {
26 | super(new Mod("BazaarNotifier", ModType.SKYBLOCK,"/icon.png", new JsonMigrator("./config/BazaarNotifier/config.json")), "bazaarnotifier.json");
27 | initialize();
28 |
29 | addListener("collectionCheck", () -> {
30 | collectionCheck = false;
31 | });
32 |
33 | collectionCheck = false; // in case the user has it enabled already, we want to force it off
34 | }
35 |
36 | public boolean firstLoad = true;
37 |
38 | @JsonName("version")
39 | public String version = BazaarNotifier.VERSION;
40 |
41 | @HUD(name = "Suggestion Module",
42 | category = "Suggestion Module"
43 | )
44 | public SuggestionModule suggestionModule = new SuggestionModule();
45 | @HUD(name = "Crafting Module",
46 | category = "Crafting Module"
47 | )
48 | public CraftingModule craftingModule = new CraftingModule();
49 | @HUD(name = "Notification Module",
50 | category = "Notification Module"
51 | )
52 | public NotificationModule notificationModule = new NotificationModule();
53 | @HUD(name = "Bank Module",
54 | category = "Bank Module"
55 | )
56 | public BankModule bankModule = new BankModule();
57 |
58 | @Switch(name = "Allow old Movement and Rescaling",
59 | category = "General",
60 | description = "Allows movement and rescaling outside the edit hud window"
61 | )
62 | public boolean legacyMovement = true;
63 | @JsonName("showChatMessages")
64 | @Switch(name = "Show Chat Messages",
65 | description = "Disables messages from Bazaar Notifier"
66 | )
67 | public boolean showChatMessages = Defaults.SEND_CHAT_MESSAGES;
68 |
69 | @JsonName("collectionCheck")
70 | @Switch(name = "Collection checks",
71 | category = "Crafting Module",
72 | description = "Only shows unlocked recipes"
73 | )
74 | public boolean collectionCheck = Defaults.COLLECTION_CHECKING;
75 |
76 |
77 | @cc.polyfrost.oneconfig.config.annotations.Color(
78 | name = "Info Color",
79 | allowAlpha = false,
80 | description = "The color for all information and less significant elements"
81 | )
82 | public OneColor infoColor = new OneColor(Color.GRAY);
83 | @cc.polyfrost.oneconfig.config.annotations.Color(name = "Item Color",
84 | allowAlpha = false,
85 | description = "The color the items will be in"
86 | )
87 | public OneColor itemColor = new OneColor(Color.CYAN);
88 | @cc.polyfrost.oneconfig.config.annotations.Color(
89 | name = "Number Color",
90 | allowAlpha = false,
91 | description = "The color the numbers will be in"
92 | )
93 | public OneColor numberColor = new OneColor(Color.MAGENTA);
94 |
95 | @SuppressWarnings("unused")
96 | @Button(name = "Reset Colors", text = "Reset")
97 | Runnable r = () ->{
98 | infoColor = new OneColor(Color.GRAY);
99 | itemColor = new OneColor(Color.CYAN);
100 | numberColor = new OneColor(Color.MAGENTA);
101 | };
102 |
103 |
104 | @SuppressWarnings("unused")
105 | @Button(
106 | name = "Reset Profit",
107 | text = "Reset",
108 | category = "Bank Module"
109 | )
110 | Runnable resetBank = BankCalculator::reset;
111 |
112 | @JsonName("lastLogin")
113 | public long lastLogin = System.currentTimeMillis();
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/handlers/ChestTickHandler.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.handlers;
2 |
3 | import dev.meyi.bn.BazaarNotifier;
4 | import dev.meyi.bn.json.Order;
5 | import dev.meyi.bn.utilities.ReflectionHelper;
6 | import dev.meyi.bn.utilities.Utils;
7 | import java.util.Arrays;
8 | import java.util.List;
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 | import net.minecraft.client.Minecraft;
12 | import net.minecraft.client.gui.inventory.GuiChest;
13 | import net.minecraft.inventory.IInventory;
14 | import net.minecraft.item.Item;
15 | import net.minecraft.item.ItemStack;
16 | import net.minecraft.util.ChatComponentText;
17 | import net.minecraft.util.EnumChatFormatting;
18 | import net.minecraft.util.StringUtils;
19 | import net.minecraftforge.client.event.GuiScreenEvent;
20 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
21 | import net.minecraftforge.fml.common.gameevent.TickEvent;
22 | import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
23 |
24 | @SuppressWarnings("unused")
25 | public class ChestTickHandler {
26 |
27 | public static String lastScreenDisplayName = "";
28 |
29 | // /blockdata x y z {CustomName:"___"} << For Custom Chest Name Testing
30 |
31 | public static void updateBazaarOrders(IInventory chest) {
32 | int[] verifiedOrders = new int[BazaarNotifier.orders.size()];
33 | ItemStack[] items = new ItemStack[chest.getSizeInventory() + 1];
34 | for (int i = 0; i < chest.getSizeInventory(); i++) {
35 | items[i] = chest.getStackInSlot(i);
36 | }
37 | items[items.length - 1] = Minecraft.getMinecraft().thePlayer.inventory.getItemStack();
38 |
39 | Pattern p = Pattern.compile("(?:\\[.*\\] )(.*)");
40 | Matcher m = p.matcher(StringUtils.stripControlCodes(Minecraft.getMinecraft().thePlayer.getDisplayName().getUnformattedText()));
41 | String playerName = null;
42 | if (m.find()) {
43 | playerName = m.group(1);
44 | } else {
45 | System.err.println("Improperly formatted player name. Aborting!");
46 | }
47 |
48 | for (ItemStack item : items) {
49 | if(item == null) continue;
50 | int itemID = Item.itemRegistry.getIDForObject(item.getItem());
51 | if ( itemID == 160 // Glass
52 | || itemID == 102 // Glass
53 | || itemID == 262 // Arrow
54 | || (itemID == 154 && StringUtils.stripControlCodes(item.getDisplayName()).equals("Claim All Coins"))) { //Hopper
55 | continue;
56 | } //Hopper
57 | List lore = Utils.getLoreFromItemStack(item);
58 |
59 | Pattern p2 = Pattern.compile("(BUY|SELL):? (.*)");
60 | Matcher m2 = p2.matcher(StringUtils.stripControlCodes(item.getDisplayName()));
61 | String displayName;
62 | Order.OrderType type;
63 | if (m2.find()) {
64 | displayName = m2.group(2);
65 | type = "sell".equalsIgnoreCase(m2.group(1)) ? Order.OrderType.SELL : Order.OrderType.BUY;
66 | } else {
67 | System.err.println("Bazaar item header incorrect. Aborting!");
68 | return;
69 | }
70 |
71 | if (BazaarNotifier.bazaarConv.containsValue(displayName)) {
72 | String priceString;
73 | if (lore.get(4).toLowerCase().contains("expire")) {
74 | priceString = StringUtils.stripControlCodes(lore.get(6)).replaceAll(",", "")
75 | .split(" ")[3];
76 | } else if (lore.get(5).toLowerCase().contains("expire")) {
77 | priceString = StringUtils.stripControlCodes(lore.get(7)).replaceAll(",", "")
78 | .split(" ")[3];
79 | } else {
80 | priceString = StringUtils.stripControlCodes(
81 | lore.get((lore.get(3).startsWith("Filled:")) ? 5 : 4).replaceAll(",", "")
82 | .split(" ")[3]);
83 | }
84 | int orderInQuestion = -1;
85 | for (int j = 0; j < BazaarNotifier.orders.size(); j++) {
86 | Order order = BazaarNotifier.orders.get(j);
87 | if (priceString.equalsIgnoreCase(order.priceString) && type.equals(
88 | order.type)) { // Todo check product also causing problems
89 | orderInQuestion = j;
90 | break;
91 | }
92 | }
93 | if (orderInQuestion != -1) {
94 | verifiedOrders[orderInQuestion] = 1;
95 | Order order = BazaarNotifier.orders.get(orderInQuestion);
96 | int totalAmount = order.startAmount;
97 | int amountLeft = Utils.getOrderAmountLeft(lore, totalAmount);
98 | if (amountLeft > 0) {
99 | order.setAmountRemaining(amountLeft);
100 | }
101 | } else if (playerName != null) {
102 | Pattern p3 = Pattern.compile("By: (?:\\[.*\\] )?(.*)");
103 | String creator = "";
104 | for (String line : lore) {
105 | Matcher m3 = p3.matcher(line);
106 | if (m3.find()) {
107 | creator = m3.group(1);
108 | break;
109 | }
110 | }
111 | if (creator.equals(playerName) || creator.isEmpty()) { //isEmpty for non Coop Islands
112 | if (lore.get(4).toLowerCase().contains("expire") || lore.get(5).toLowerCase().contains("expire")) {
113 | continue;
114 | }
115 | String totalAmount = lore.get(2).split(" ")[2];
116 | int startAmount = Integer.parseInt(totalAmount.substring(0, totalAmount.length()-1).replace(",", ""));
117 | Order newOrder = new Order(displayName, startAmount, Double.parseDouble(priceString), priceString, type);
118 | newOrder.setAmountRemaining(Utils.getOrderAmountLeft(lore, startAmount));
119 | if (newOrder.getAmountRemaining() != 0) {
120 | BazaarNotifier.orders.add(newOrder);
121 | verifiedOrders = Arrays.copyOf(verifiedOrders, verifiedOrders.length + 1);
122 | verifiedOrders[verifiedOrders.length-1] = 1;
123 | }
124 | }
125 | }
126 | } else {
127 | System.out.println(BazaarNotifier.orders);
128 | System.err.println("Some orders weren't found! Bad display name: " + displayName);
129 | return;
130 | }
131 | }
132 | for (int i = verifiedOrders.length - 1; i >= 0; i--) {
133 | if (verifiedOrders[i] == 0) {
134 | BazaarNotifier.orders.remove(i);
135 | }
136 | }
137 | }
138 |
139 | @SubscribeEvent
140 | public void onChestTick(TickEvent e) {
141 | try {
142 | if (e.phase == Phase.END) {
143 | if (Minecraft.getMinecraft().currentScreen instanceof GuiChest && BazaarNotifier.inBazaar
144 | && BazaarNotifier.activeBazaar) {
145 |
146 | IInventory chest = ReflectionHelper.getLowerChestInventory(
147 | (GuiChest) Minecraft.getMinecraft().currentScreen);
148 | if (chest == null) {
149 | return;
150 | }
151 | String chestName = chest.getDisplayName().getUnformattedText().toLowerCase();
152 |
153 | if (chest.hasCustomName() && !lastScreenDisplayName.equalsIgnoreCase(chestName)) {
154 | if (chestName.equals("confirm buy order") || chestName.equals("confirm sell offer")) {
155 | if (chest.getStackInSlot(13) != null) {
156 | if (orderConfirmation(chest)) { // Don´t reset the lastScreenDisplayName to retry in the next tick
157 | lastScreenDisplayName = StringUtils.stripControlCodes(
158 | chest.getDisplayName().getUnformattedText());
159 | }
160 | }
161 |
162 | } else if (chestName.contains("bazaar orders")) {
163 | if (chest.getStackInSlot(chest.getSizeInventory() - 5) != null &&
164 | Item.itemRegistry.getIDForObject(
165 | chest.getStackInSlot(chest.getSizeInventory() - 5).getItem()) == 262) {
166 | lastScreenDisplayName = StringUtils.stripControlCodes(
167 | chest.getDisplayName().getUnformattedText());
168 | updateBazaarOrders(chest);
169 | }
170 | } else if (chestName.contains("bazaar")) {
171 | lastScreenDisplayName = StringUtils.stripControlCodes(
172 | chest.getDisplayName().getUnformattedText());
173 | }
174 | }
175 | } else if (!BazaarNotifier.inBazaar) { // if you aren't in the bazaar, this should be clear
176 | ChestTickHandler.lastScreenDisplayName = "";
177 | }
178 | }
179 | }catch (Exception ex){
180 | Minecraft.getMinecraft().thePlayer.addChatMessage(
181 | new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED +
182 | "The onChestTick method ran into an error. Please report this in the discord server"));
183 | ex.printStackTrace();
184 | }
185 | }
186 |
187 | /**
188 | * @return success
189 | */
190 | private boolean orderConfirmation(IInventory chest) {
191 |
192 | if (chest.getStackInSlot(13) != null) {
193 | String priceString = "";
194 | String product = "";
195 | double price = 0;
196 |
197 | try {
198 | priceString = StringUtils.stripControlCodes(
199 | chest.getStackInSlot(13).getTagCompound().getCompoundTag("display").getTagList("Lore", 8)
200 | .getStringTagAt(2)).split(" ")[3].replaceAll(",", "");
201 | price = Double.parseDouble(priceString);
202 |
203 | product = StringUtils.stripControlCodes(
204 | chest.getStackInSlot(13).getTagCompound().getCompoundTag("display").getTagList("Lore", 8)
205 | .getStringTagAt(4)).split("x ", 2)[1];
206 | }catch (Exception e){
207 | return false;
208 | }
209 | String productName;
210 |
211 | if (!BazaarNotifier.bazaarConv.containsValue(product)) {
212 | String[] possibleConversion = Utils.getItemIdFromName(product);
213 | productName = possibleConversion[1];
214 |
215 | if (!possibleConversion[0].equals(product) && !productName.isEmpty()) {
216 | BazaarNotifier.bazaarConv.put(productName, product);
217 | Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
218 | BazaarNotifier.prefix + EnumChatFormatting.RED
219 | + "A possible conversion was found. Please report this to the discord server:"
220 | + EnumChatFormatting.GRAY + " \""
221 | + productName + "\" - \"" + product + "\" \"" + possibleConversion[0] + "\"."));
222 | } else {
223 | Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
224 | BazaarNotifier.prefix + EnumChatFormatting.RED
225 | + "No item conversion was found for that item. Please report this to the discord server: "
226 | + EnumChatFormatting.GRAY + "\""
227 | + product + "\"."));
228 | }
229 | } else {
230 | productName = BazaarNotifier.bazaarConv.inverse().get(product);
231 | }
232 |
233 | if (BazaarNotifier.bazaarConv.containsKey(productName)) {
234 | String productWithAmount = StringUtils.stripControlCodes(
235 | chest.getStackInSlot(13).getTagCompound().getCompoundTag("display")
236 | .getTagList("Lore", 8).getStringTagAt(4)).split(": ")[1];
237 |
238 | int amount = Integer.parseInt(StringUtils.stripControlCodes(
239 | chest.getStackInSlot(13).getTagCompound().getCompoundTag("display")
240 | .getTagList("Lore", 8).getStringTagAt(4)).split(": ")[1].split("x ")[0].replaceAll(
241 | ",", ""));
242 |
243 | EventHandler.productVerify[0] = productName;
244 | EventHandler.productVerify[1] = productWithAmount;
245 | Order.OrderType type =
246 | StringUtils.stripControlCodes(chest.getDisplayName().getUnformattedText())
247 | .equalsIgnoreCase("Confirm Sell Offer") ? Order.OrderType.SELL
248 | : Order.OrderType.BUY;
249 | EventHandler.verify = new Order(product, amount, price, priceString, type);
250 | }
251 | }
252 | return true;
253 | }
254 | @SubscribeEvent
255 | public void renderInChest(GuiScreenEvent.BackgroundDrawnEvent e){
256 | BazaarNotifier.modules.drawAllGui();
257 | }
258 | }
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/handlers/EventHandler.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.handlers;
2 |
3 | import cc.polyfrost.oneconfig.gui.OneConfigGui;
4 | import cc.polyfrost.oneconfig.gui.pages.ModConfigPage;
5 | import cc.polyfrost.oneconfig.platform.Platform;
6 | import dev.meyi.bn.BazaarNotifier;
7 | import dev.meyi.bn.json.Order;
8 | import dev.meyi.bn.modules.calc.BankCalculator;
9 | import dev.meyi.bn.utilities.ReflectionHelper;
10 | import net.minecraft.client.Minecraft;
11 | import net.minecraft.client.gui.inventory.GuiChest;
12 | import net.minecraft.client.gui.inventory.GuiEditSign;
13 | import net.minecraft.inventory.IInventory;
14 | import net.minecraft.util.StringUtils;
15 | import net.minecraftforge.client.event.ClientChatReceivedEvent;
16 | import net.minecraftforge.client.event.GuiOpenEvent;
17 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
18 | import net.minecraftforge.fml.common.gameevent.TickEvent;
19 | import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
20 |
21 | @SuppressWarnings("unused")
22 | public class EventHandler {
23 |
24 | static Order verify = null;
25 | static String[] productVerify = new String[2];
26 |
27 | @SubscribeEvent
28 | public void bazaarChatHandler(ClientChatReceivedEvent e) {
29 |
30 | if (!BazaarNotifier.activeBazaar) {
31 | return;
32 | }
33 | String message = StringUtils.stripControlCodes(e.message.getUnformattedText());
34 |
35 | if (message.startsWith("[Bazaar] Claimed") || message.startsWith("[Bazaar] Bought")
36 | || message.startsWith("[Bazaar] Sold")) {
37 | BankCalculator.evaluate(message);
38 | }
39 |
40 | if (message.startsWith("Buy Order Setup!") || message.startsWith("Sell Offer Setup!")
41 | || message.startsWith("[Bazaar] Buy Order Setup!") || message.startsWith(
42 | "[Bazaar] Sell Offer Setup!")) {
43 | if (productVerify[0] != null && productVerify[1] != null && productVerify[0].equals(
44 | BazaarNotifier.bazaarConv.inverse().get(message.split("x ", 2)[1].split(" for ")[0]))
45 | && productVerify[1].equals(message.split("! ")[1].split(" for ")[0])) {
46 | BazaarNotifier.orders.add(verify);
47 | BankCalculator.evaluateCapHit(verify);
48 | verify = null;
49 | productVerify = new String[2];
50 | }
51 | } else if (message.startsWith("[Bazaar] Your ") && message.endsWith(" was filled!")) {
52 | String item = message.split("x ", 2)[1].split(" was ")[0];
53 | int amount = Integer.parseInt(
54 | message.split(" for ")[1].split("x ", 2)[0].replaceAll(",", ""));
55 | int orderToRemove = 0;
56 | boolean found = false;
57 | double edgePrice;
58 | if (message.startsWith("[Bazaar] Your Buy Order")) {
59 | edgePrice = Double.MIN_VALUE;
60 | for (int i = 0; i < BazaarNotifier.orders.size(); i++) {
61 | Order order = BazaarNotifier.orders.get(i);
62 | if (order.product.equalsIgnoreCase(item) && order.startAmount == amount
63 | && order.type.equals(Order.OrderType.BUY) && order.pricePerUnit > edgePrice) {
64 | edgePrice = order.pricePerUnit;
65 | orderToRemove = i;
66 | found = true;
67 |
68 | }
69 | }
70 | } else if (message.startsWith("[Bazaar] Your Sell Offer")) {
71 | edgePrice = Double.MAX_VALUE;
72 | for (int i = 0; i < BazaarNotifier.orders.size(); i++) {
73 | Order order = BazaarNotifier.orders.get(i);
74 | if (order.product.equalsIgnoreCase(item) && order.startAmount == amount
75 | && order.type.equals(Order.OrderType.SELL) && order.pricePerUnit < edgePrice) {
76 |
77 | edgePrice = order.pricePerUnit;
78 | orderToRemove = i;
79 | found = true;
80 | }
81 | }
82 | }
83 | if (found) {
84 | BazaarNotifier.orders.remove(orderToRemove);
85 | } else {
86 | System.err.println("There is some error in removing your order from the list!!!");
87 | }
88 | } else if (message.startsWith("Cancelled!") || message.startsWith("[Bazaar] Cancelled!")) {
89 | double refund = 0;
90 | int refundAmount = 0;
91 | String itemRefunded = "";
92 | if (message.endsWith("Buy Order!")) {
93 | refund = Double.parseDouble(
94 | message.split("Refunded ")[1].split(" coins")[0].replaceAll(",", ""));
95 | if (refund >= 10000) {
96 | refund = Math.round(refund);
97 | }
98 | } else if (message.endsWith("Sell Offer!")) {
99 | refundAmount = Integer.parseInt(
100 | message.split("Refunded ")[1].split("x ", 2)[0].replaceAll(",", ""));
101 | itemRefunded = message.split("x ", 2)[1].split(" from")[0];
102 |
103 | }
104 | for (int i = 0; i < BazaarNotifier.orders.size(); i++) {
105 | Order order = BazaarNotifier.orders.get(i);
106 | if (message.endsWith("Buy Order!") && order.type.equals(Order.OrderType.BUY)) {
107 | double remaining =
108 | (refund >= 10000 ? Math.round(order.orderValue) : order.orderValue) - refund;
109 | if (remaining <= 1 && remaining >= 0) {
110 | BazaarNotifier.orders.remove(i);
111 | break;
112 | }
113 | } else if (message.endsWith("Sell Offer!") && order.type.equals(Order.OrderType.SELL)) {
114 | if (order.product.equalsIgnoreCase(itemRefunded)
115 | && order.getAmountRemaining() == refundAmount) {
116 | BazaarNotifier.orders.remove(i);
117 | break;
118 | }
119 | }
120 | }
121 | } else if (message.startsWith("Bazaar! Claimed ") || message.startsWith("[Bazaar] Claimed")) {
122 | ChestTickHandler.lastScreenDisplayName = "";
123 | }
124 | }
125 |
126 | @SubscribeEvent
127 | public void menuOpenedEvent(GuiOpenEvent e) {
128 | if (e.gui instanceof GuiChest) {
129 | IInventory chest = ReflectionHelper.getLowerChestInventory((GuiChest) e.gui);
130 | if (chest != null && ((chest.hasCustomName() && (
131 | StringUtils.stripControlCodes(chest.getDisplayName().getUnformattedText())
132 | .startsWith("Bazaar") || StringUtils.stripControlCodes(
133 | chest.getDisplayName().getUnformattedText())
134 | .equalsIgnoreCase("How much do you want to pay?") || StringUtils.stripControlCodes(
135 | chest.getDisplayName().getUnformattedText())
136 | .matches("Confirm (Buy|Sell) (Order|Offer)")) || StringUtils.stripControlCodes(
137 | chest.getDisplayName().getUnformattedText()).contains("Bazaar"))
138 | || BazaarNotifier.forceRender)) {
139 | BazaarNotifier.inBazaar = true;
140 | }
141 | } else if (e.gui == null || e.gui instanceof GuiEditSign) {
142 | BazaarNotifier.inBazaar = false;
143 | }
144 | }
145 |
146 | @SubscribeEvent
147 | public void disconnectEvent(ClientDisconnectionFromServerEvent e) {
148 | BazaarNotifier.inBazaar = false;
149 | }
150 |
151 | @SubscribeEvent
152 | public void renderEvent(TickEvent e) {
153 | if (BazaarNotifier.guiToOpen.contains("settings")) {
154 | if (Platform.getGuiPlatform().getCurrentScreen() == null) {
155 | Minecraft.getMinecraft().displayGuiScreen(OneConfigGui.create());
156 | BazaarNotifier.guiToOpen = "settings-mod";
157 | } else if (BazaarNotifier.guiToOpen.equals("settings-mod")
158 | && Platform.getGuiPlatform().getCurrentScreen() instanceof OneConfigGui) {
159 | OneConfigGui.INSTANCE.openPage(
160 | new ModConfigPage(BazaarNotifier.config.mod.defaultPage, true),
161 | new cc.polyfrost.oneconfig.gui.animations.DummyAnimation(2128), false);
162 | BazaarNotifier.guiToOpen = "";
163 | }
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/handlers/MouseHandler.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.handlers;
2 |
3 | import dev.meyi.bn.BazaarNotifier;
4 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
5 | import net.minecraftforge.fml.common.gameevent.TickEvent;
6 | import org.lwjgl.input.Mouse;
7 |
8 |
9 | public class MouseHandler {
10 |
11 | public static int mouseWheelMovement = 0;
12 | int tick = 0;
13 | boolean inPageFlip = false;
14 |
15 |
16 | @SubscribeEvent
17 | public void mouseActionCheck(TickEvent e) {
18 | if (e.phase == TickEvent.Phase.START) {
19 | if (BazaarNotifier.inBazaar) {
20 | if(BazaarNotifier.config.legacyMovement) {
21 | BazaarNotifier.modules.movementCheck();
22 | }
23 | if (tick >= 8 && !inPageFlip) { // 2.5 times per second
24 | inPageFlip = true;
25 | BazaarNotifier.modules.pageFlipCheck();
26 | if (BazaarNotifier.config.legacyMovement) {
27 | BazaarNotifier.modules.rescaleCheck();
28 | }
29 | BazaarNotifier.modules.shiftSettingCheck();
30 | mouseWheel();
31 |
32 | tick = 0;
33 | inPageFlip = false;
34 | }
35 | tick++;
36 | } else {
37 | tick = 0;
38 | }
39 | }
40 | }
41 |
42 |
43 | public void mouseWheel() {
44 | mouseWheelMovement = (int) Math.round((double) Mouse.getDWheel() * -1 / 100);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/handlers/UpdateHandler.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.handlers;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.JsonObject;
5 | import dev.meyi.bn.BazaarNotifier;
6 | import java.io.BufferedReader;
7 | import java.io.IOException;
8 | import java.io.InputStreamReader;
9 | import java.util.concurrent.ScheduledThreadPoolExecutor;
10 | import java.util.concurrent.TimeUnit;
11 | import java.util.regex.Matcher;
12 | import java.util.regex.Pattern;
13 | import net.minecraft.client.Minecraft;
14 | import net.minecraft.event.ClickEvent;
15 | import net.minecraft.event.ClickEvent.Action;
16 | import net.minecraft.util.ChatComponentText;
17 | import net.minecraft.util.EnumChatFormatting;
18 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
19 | import net.minecraftforge.fml.common.network.FMLNetworkEvent;
20 | import org.apache.commons.io.IOUtils;
21 | import org.apache.http.client.methods.HttpGet;
22 | import org.apache.http.impl.client.HttpClientBuilder;
23 |
24 | public class UpdateHandler {
25 |
26 | boolean firstJoin = true;
27 |
28 | @SubscribeEvent
29 | public void onPlayerJoinEvent(FMLNetworkEvent.ClientConnectedToServerEvent event) {
30 | if (firstJoin) {
31 | firstJoin = false;
32 | new ScheduledThreadPoolExecutor(1).schedule(() -> {
33 | Gson gson = new Gson();
34 | try {
35 | String[] currentTag;
36 | String[] latestTag;
37 | int currentBeta = 0;
38 | int latestBeta = 0;
39 | boolean beta = false;
40 | Pattern p = Pattern
41 | .compile("\"?([0-9]+\\.[0-9]+\\.[0-9]+)-beta([0-9]+)\"?$", Pattern.MULTILINE);
42 | Matcher m = p.matcher(BazaarNotifier.VERSION);
43 | if (m.find()) {
44 | beta = true;
45 | String version = m.group(1);
46 | currentTag = version.split("\\.");
47 | currentBeta = Integer.parseInt(m.group(2));
48 |
49 | // Using the same pattern to check can be risky if there is another version in the build.gradle that also matches, but so far that isn't a problem.
50 |
51 | String buildGradle;
52 | try {
53 | buildGradle = IOUtils.toString(new BufferedReader(new InputStreamReader(
54 | HttpClientBuilder.create().build().execute(new HttpGet(
55 | "https://raw.githubusercontent.com/symt/BazaarNotifier/beta/build.gradle.kts"))
56 | .getEntity().getContent())));
57 | } catch (IOException e) {
58 | Minecraft.getMinecraft().thePlayer.addChatMessage(
59 | new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
60 | + "There was an error reading the version. Make sure you aren't on an old beta version. /bn discord"));
61 | e.printStackTrace();
62 | return;
63 | }
64 | Matcher m2 = p.matcher(buildGradle);
65 | if (m2.find()) {
66 | latestTag = m2.group(1).split("\\.");
67 | latestBeta = Integer.parseInt(m2.group(2));
68 | } else {
69 | Minecraft.getMinecraft().thePlayer.addChatMessage(
70 | new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
71 | + "There was an error reading the beta version. No version matched in the build.gradle file."));
72 | return;
73 | }
74 | } else {
75 | JsonObject json = gson
76 | .fromJson(IOUtils.toString(new BufferedReader(new InputStreamReader(
77 | HttpClientBuilder.create().build().execute(new HttpGet(
78 | "https://api.github.com/repos/symt/BazaarNotifier/releases/latest"))
79 | .getEntity().getContent()))),
80 | JsonObject.class).getAsJsonObject();
81 | latestTag = json.get("tag_name").getAsString().split("\\.");
82 | currentTag = BazaarNotifier.VERSION.split("\\.");
83 | }
84 |
85 | // Assuming the user is on the correct beta version, this will pass without any chat messages.
86 |
87 | if (latestTag.length == 3 && currentTag.length == 3) {
88 | for (int i = 0; i < latestTag.length; i++) {
89 | int latestCheck = Integer.parseInt(latestTag[i]);
90 | int currentCheck = Integer.parseInt(currentTag[i]);
91 |
92 | if (latestCheck != currentCheck) {
93 | if (latestCheck < currentCheck) {
94 | Minecraft.getMinecraft().thePlayer.addChatMessage(
95 | new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
96 | + "This version hasn't been released yet. Please report any bugs that you come across."));
97 | } else {
98 | ChatComponentText updateLink = new ChatComponentText(
99 | EnumChatFormatting.DARK_RED + "" + EnumChatFormatting.BOLD
100 | + "[UPDATE LINK]");
101 | updateLink
102 | .setChatStyle(updateLink.getChatStyle().setChatClickEvent(new ClickEvent(
103 | Action.OPEN_URL,
104 | "https://github.com/symt/BazaarNotifier/releases/latest")));
105 | Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
106 | BazaarNotifier.prefix + EnumChatFormatting.RED
107 | + "The mod version that you're on is outdated. Please update for the best profits: ")
108 | .appendSibling(updateLink));
109 | }
110 | beta = false; // Versions don't match, so betas don't matter
111 | break;
112 | }
113 | }
114 | }
115 |
116 | if (beta) {
117 | if (currentBeta > latestBeta) {
118 | Minecraft.getMinecraft().thePlayer.addChatMessage(
119 | new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
120 | + "This beta version hasn't been released yet. Please report any bugs that you come across."));
121 | } else if (currentBeta < latestBeta) {
122 | Minecraft.getMinecraft().thePlayer.addChatMessage(
123 | new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.RED
124 | + "You are on an outdated beta version. Please update via the discord server. Run /bn discord for the link"));
125 | } else {
126 | Minecraft.getMinecraft().thePlayer.addChatMessage(
127 | new ChatComponentText(BazaarNotifier.prefix + EnumChatFormatting.GREEN
128 | + "You are on a beta version. Please report any bugs you come across in the discord server."));
129 | }
130 | }
131 | } catch (IOException e) {
132 | e.printStackTrace();
133 | }
134 | }, 3, TimeUnit.SECONDS);
135 | }
136 | }
137 | }
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/json/Exchange.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.json;
2 |
3 | import dev.meyi.bn.BazaarNotifier;
4 | import dev.meyi.bn.json.Order.OrderType;
5 | import dev.meyi.bn.modules.calc.CraftingCalculator;
6 | import java.util.Map;
7 |
8 | public class Exchange {
9 |
10 | private final String productId;
11 | private final double pricePerUnit;
12 | private int amount;
13 | private final OrderType type;
14 |
15 | private final Map craftingResources;
16 |
17 | public Exchange(OrderType type, String productId, double pricePerUnit, int amount) {
18 | this.type = type;
19 | this.productId = productId;
20 | this.pricePerUnit = pricePerUnit;
21 | this.amount = amount;
22 |
23 | if (canCraft()) {
24 | craftingResources = CraftingCalculator.getMaterialsMap(productId);
25 | } else {
26 | craftingResources = null;
27 | }
28 | }
29 |
30 | public String getProductId() {
31 | return productId;
32 | }
33 |
34 | public Map getCraftingResources() {
35 | return craftingResources;
36 | }
37 |
38 | public double getPricePerUnit() {
39 | return pricePerUnit;
40 | }
41 |
42 | public int getAmount() {
43 | return amount;
44 | }
45 |
46 | public void removeAmount(int remove) {
47 | this.amount -= remove;
48 | }
49 |
50 | public void addAmount(int add) {
51 | this.amount += add;
52 | }
53 |
54 | public OrderType getType() {
55 | return type;
56 | }
57 |
58 | public boolean matchesOrder(Exchange exchange) {
59 | return this.type != exchange.type && this.productId.equals(exchange.productId);
60 | }
61 |
62 | public boolean canCraft() {
63 | return type == OrderType.SELL && BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
64 | .has(productId);
65 | }
66 |
67 | @Override
68 | public boolean equals(Object obj) {
69 | return (obj instanceof Exchange) && this.pricePerUnit == ((Exchange) obj).pricePerUnit
70 | && this.productId.equals(((Exchange) obj).productId);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/json/Order.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.json;
2 |
3 |
4 | import dev.meyi.bn.BazaarNotifier;
5 | import dev.meyi.bn.json.resp.BazaarItem;
6 | import dev.meyi.bn.utilities.RenderUtils;
7 |
8 | public class Order {
9 |
10 | public String product;
11 | public int startAmount;
12 | public double pricePerUnit;
13 | public String priceString;
14 | public OrderStatus orderStatus = OrderStatus.SEARCHING;
15 | public double orderValue;
16 | public OrderType type;
17 | public long creationTime;
18 | private int amountRemaining;
19 |
20 | public Order(String product, int startAmount, double pricePerUnit, String priceString,
21 | OrderType type) {
22 | this.product = product;
23 | this.startAmount = startAmount;
24 | amountRemaining = startAmount;
25 | this.pricePerUnit = pricePerUnit;
26 | this.priceString = priceString;
27 | this.type = type;
28 | orderValue = startAmount * pricePerUnit;
29 | creationTime = System.currentTimeMillis();
30 | }
31 |
32 | public Order(String product, OrderType type, double pricePerUnit, int startAmount) {
33 | this.product = product;
34 | this.type = type;
35 | this.pricePerUnit = pricePerUnit;
36 | this.startAmount = startAmount;
37 | }
38 |
39 | public int getAmountRemaining() {
40 | return amountRemaining;
41 | }
42 |
43 | public void setAmountRemaining(int amountRemaining) {
44 | this.amountRemaining = amountRemaining;
45 | orderValue = amountRemaining * pricePerUnit;
46 | }
47 |
48 | public boolean matches(Order other) {
49 | return other.type == this.type && other.product.equals(this.product)
50 | && other.startAmount == this.startAmount &&
51 | other.pricePerUnit == this.pricePerUnit;
52 | }
53 |
54 | public String getProductId() {
55 | return BazaarNotifier.bazaarConv.inverse().get(product);
56 | }
57 |
58 | public void updateStatus() {
59 | OrderStatus newOrderStatus;
60 | if (!BazaarNotifier.activeBazaar) {
61 | return;
62 | }
63 | if (OrderType.BUY.equals(this.type)) {
64 | if (BazaarNotifier.bazaarDataRaw.products.get(getProductId()).sell_summary.isEmpty()) {
65 | orderStatus = OrderStatus.SEARCHING;
66 | return;
67 | }
68 | BazaarItem.BazaarSubItem bazaarSubItem = BazaarNotifier.bazaarDataRaw.products
69 | .get(getProductId()).sell_summary.get(0);
70 | if(creationTime > BazaarNotifier.bazaarDataRaw.lastUpdated){
71 | newOrderStatus = OrderStatus.SEARCHING;
72 | }
73 | else if (this.pricePerUnit < bazaarSubItem.pricePerUnit) {
74 | newOrderStatus = OrderStatus.OUTDATED;
75 | } else if (this.pricePerUnit == bazaarSubItem.pricePerUnit
76 | && this.startAmount >= bazaarSubItem.amount
77 | && bazaarSubItem.orders == 1) { //&& this.amountRemaining <= bazaarSubItem.amount
78 | newOrderStatus = OrderStatus.BEST;
79 | } else if (this.pricePerUnit > bazaarSubItem.pricePerUnit) {
80 | newOrderStatus = OrderStatus.SEARCHING;
81 | } else if (this.pricePerUnit == bazaarSubItem.pricePerUnit && bazaarSubItem.orders == 1) {
82 | newOrderStatus = OrderStatus.SEARCHING;
83 | } else if (this.pricePerUnit == bazaarSubItem.pricePerUnit && bazaarSubItem.orders > 1 &&
84 | BazaarNotifier.orders.stream().filter(order -> this.product.equals(order.product)
85 | && this.pricePerUnit == order.pricePerUnit).count() != bazaarSubItem.orders) {
86 | newOrderStatus = OrderStatus.MATCHED;
87 | } else {
88 | newOrderStatus = OrderStatus.BEST;
89 | }
90 | } else {
91 | if (BazaarNotifier.bazaarDataRaw.products.get(getProductId()).buy_summary.isEmpty()) {
92 | orderStatus = OrderStatus.SEARCHING;
93 | return;
94 | }
95 | BazaarItem.BazaarSubItem bazaarSubItem = BazaarNotifier.bazaarDataRaw.products
96 | .get(getProductId()).buy_summary.get(0);
97 | if(creationTime > BazaarNotifier.bazaarDataRaw.lastUpdated){
98 | newOrderStatus = OrderStatus.SEARCHING;
99 | }
100 | else if (this.pricePerUnit > bazaarSubItem.pricePerUnit) {
101 | newOrderStatus = OrderStatus.OUTDATED;
102 | } else if (this.pricePerUnit == bazaarSubItem.pricePerUnit
103 | && this.startAmount >= bazaarSubItem.amount && bazaarSubItem.orders == 1) {
104 | newOrderStatus = OrderStatus.BEST;
105 | } else if (this.pricePerUnit < bazaarSubItem.pricePerUnit) {
106 | newOrderStatus = OrderStatus.SEARCHING;
107 | } else if (this.pricePerUnit == bazaarSubItem.pricePerUnit && bazaarSubItem.orders == 1) {
108 | newOrderStatus = OrderStatus.SEARCHING;
109 | } else if (this.pricePerUnit == bazaarSubItem.pricePerUnit && bazaarSubItem.orders > 1 &&
110 | BazaarNotifier.orders.stream().filter(order -> this.product.equals(order.product)
111 | && this.pricePerUnit == order.pricePerUnit).count() != bazaarSubItem.orders) {
112 | newOrderStatus = OrderStatus.MATCHED;
113 | } else {
114 | newOrderStatus = OrderStatus.BEST;
115 | }
116 | }
117 | if (this.orderStatus != newOrderStatus) {
118 | if (OrderStatus.BEST.equals(newOrderStatus) && this.orderStatus != OrderStatus.SEARCHING) {
119 | RenderUtils.chatNotification(this, "REVIVED");
120 | } else if (OrderStatus.MATCHED.equals(newOrderStatus)) {
121 | RenderUtils.chatNotification(this, "MATCHED");
122 | } else if (OrderStatus.OUTDATED.equals(newOrderStatus)) {
123 | RenderUtils.chatNotification(this, "OUTDATED");
124 | }
125 | this.orderStatus = newOrderStatus;
126 | }
127 | }
128 |
129 | public enum OrderStatus {BEST, MATCHED, OUTDATED, SEARCHING}
130 |
131 | public enum OrderType {
132 | BUY("Buy Order"),
133 | SELL("Sell Offer");
134 | public final String longName;
135 |
136 | OrderType(String longName) {
137 | this.longName = longName;
138 | }
139 | }
140 | }
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/json/resp/BazaarItem.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.json.resp;
2 |
3 | import java.util.List;
4 |
5 | public class BazaarItem {
6 |
7 | public String product_id;
8 |
9 | public List sell_summary;
10 | public List buy_summary;
11 |
12 | public BazaarItemSummary quick_status;
13 |
14 | public BazaarItem(String product_id, List sell_summary,
15 | List buy_summary,
16 | BazaarItemSummary quick_status) {
17 | this.product_id = product_id;
18 | this.sell_summary = sell_summary;
19 | this.buy_summary = buy_summary;
20 | this.quick_status = quick_status;
21 | }
22 |
23 | public static class BazaarSubItem {
24 |
25 | public int amount;
26 | public double pricePerUnit;
27 | public int orders;
28 |
29 | public BazaarSubItem(int amount, double pricePerUnit, int orders) {
30 | this.amount = amount;
31 | this.pricePerUnit = pricePerUnit;
32 | this.orders = orders;
33 | }
34 |
35 | public double getPriceWithTax() {
36 | return pricePerUnit * 0.9875d;
37 | }
38 | }
39 |
40 | public static class BazaarItemSummary {
41 |
42 | public String productId;
43 | public double sellPrice;
44 | public long sellVolume;
45 | public long sellMovingWeek;
46 | public long sellOrders;
47 | public double buyPrice;
48 | public long buyVolume;
49 | public long buyMovingWeek;
50 | public long buyOrders;
51 |
52 | public BazaarItemSummary(String productId, double sellPrice, long sellVolume,
53 | long sellMovingWeek, long sellOrders,
54 | double buyPrice, long buyVolume, long buyMovingWeek, long buyOrders) {
55 | this.productId = productId;
56 | this.sellPrice = sellPrice;
57 | this.sellVolume = sellVolume;
58 | this.sellMovingWeek = sellMovingWeek;
59 | this.sellOrders = sellOrders;
60 | this.buyPrice = buyPrice;
61 | this.buyVolume = buyVolume;
62 | this.buyMovingWeek = buyMovingWeek;
63 | this.buyOrders = buyOrders;
64 | }
65 |
66 | }
67 | }
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/json/resp/BazaarResponse.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.json.resp;
2 |
3 | import java.util.Map;
4 |
5 | public class BazaarResponse {
6 |
7 | public boolean success;
8 | public long lastUpdated;
9 |
10 | public Map products;
11 |
12 | public BazaarResponse(boolean success, long lastUpdated, Map products) {
13 | this.success = success;
14 | this.lastUpdated = lastUpdated;
15 | this.products = products;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/Module.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules;
2 |
3 | import cc.polyfrost.oneconfig.config.annotations.Switch;
4 | import cc.polyfrost.oneconfig.gui.OneConfigGui;
5 | import cc.polyfrost.oneconfig.hud.Hud;
6 | import cc.polyfrost.oneconfig.platform.GuiPlatform;
7 | import cc.polyfrost.oneconfig.platform.Platform;
8 | import dev.meyi.bn.BazaarNotifier;
9 | import net.minecraft.client.Minecraft;
10 | import net.minecraft.client.gui.Gui;
11 | import net.minecraft.client.gui.ScaledResolution;
12 | import org.apache.commons.lang3.StringUtils;
13 | import org.lwjgl.input.Mouse;
14 | import org.lwjgl.opengl.Display;
15 |
16 |
17 | public abstract class Module extends Hud {
18 | protected transient int shift = 0;
19 | protected transient boolean moving = false;
20 | protected transient boolean needsToMove = false;
21 | protected transient int mouseWheelShift = 0;
22 | protected transient int padding = 3;
23 | private transient int lastMouseX, lastMouseY;
24 |
25 | protected transient String longestString = "";
26 |
27 |
28 |
29 | @Switch(name="Show Module Outside of Bazaar",
30 | category = "General"
31 | )
32 | protected boolean showEverywhere = false;
33 |
34 | public Module() {
35 | super(true, 0, 0, 0, 1);
36 | }
37 |
38 | @Override
39 | protected boolean shouldShow() {
40 | if(enabled) {
41 | GuiPlatform guiPlatform = Platform.getGuiPlatform();
42 | if(BazaarNotifier.inBazaar){
43 | //Drawing at this case is handled by the ChestTickHandler to keep it above the gray gui background;
44 | return false;
45 | }
46 | if (showInGuis && (guiPlatform.getCurrentScreen() instanceof OneConfigGui)) return false;
47 | if (showInChat && guiPlatform.isInChat()) return true;
48 | if (showInDebug && guiPlatform.isInDebug()) return true;
49 | if (showInGuis && guiPlatform.getCurrentScreen() != null) return true;
50 | return showEverywhere;
51 | }else {
52 | return false;
53 | }
54 | }
55 | public abstract void draw();
56 |
57 | protected abstract void reset();
58 |
59 | public abstract String name();
60 |
61 | protected abstract boolean shouldDrawBounds();
62 |
63 | protected abstract int getMaxShift();
64 |
65 | public void handleMovement() {
66 | if (moving) {
67 | position.setPosition(position.getX() + getMouseCoordinateX() - lastMouseX, position.getY() + getMouseCoordinateY() - lastMouseY);
68 | }
69 | moving = true;
70 | lastMouseX = getMouseCoordinateX();
71 | lastMouseY = getMouseCoordinateY();
72 | }
73 |
74 | public void drawBounds() {
75 | if (shouldDrawBounds()) {
76 | Gui.drawRect((int)position.getX(), (int)position.getY(),
77 | (int)position.getRightX(), (int)position.getBottomY(), 0x66000000);
78 | }
79 | }
80 |
81 | public boolean inMovementBox() {
82 | return (getMouseCoordinateX() >= position.getX()
83 | && getMouseCoordinateY() >= position.getY()
84 | && getMouseCoordinateX() <= position.getRightX()
85 | && getMouseCoordinateY() <= position.getBottomY());
86 | }
87 |
88 |
89 | protected int getMouseCoordinateX() {
90 | return Mouse.getX() / new ScaledResolution(Minecraft.getMinecraft()).getScaleFactor();
91 | }
92 |
93 | protected int getMouseCoordinateY() {
94 | return (Display.getHeight() - Mouse.getY()) / new ScaledResolution(Minecraft.getMinecraft())
95 | .getScaleFactor();
96 | }
97 |
98 | public String getReadableName() {
99 | String name = StringUtils.lowerCase(name());
100 | return StringUtils.capitalize(name) + " Module";
101 | }
102 |
103 | public void setActive(boolean active) {
104 | enabled = active;
105 | }
106 |
107 | public float getModuleWidth(){
108 | return this.getWidth(scale, false);
109 | }
110 |
111 | public float getModuleHeight(){
112 | return this.getHeight(scale, false); }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/ModuleList.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules;
2 |
3 | import dev.meyi.bn.BazaarNotifier;
4 | import dev.meyi.bn.handlers.MouseHandler;
5 | import java.util.ArrayList;
6 | import org.lwjgl.input.Keyboard;
7 | import org.lwjgl.input.Mouse;
8 |
9 | public class ModuleList extends ArrayList {
10 |
11 | private Module movingModule = null;
12 |
13 | public ModuleList() {
14 | add(BazaarNotifier.config.suggestionModule);
15 | add(BazaarNotifier.config.bankModule);
16 | add(BazaarNotifier.config.notificationModule);
17 | add(BazaarNotifier.config.craftingModule);
18 | }
19 |
20 | public void rescaleCheck() {
21 | for (Module m : this) {
22 | if (m.inMovementBox() && Keyboard.isKeyDown(29) && !Keyboard.isKeyDown(42)) {
23 | float newScale = m.getScale() + (float) MouseHandler.mouseWheelMovement / 20;
24 | m.setScale(Math.max(newScale, 0.1f),false);
25 | }
26 | }
27 | }
28 |
29 | public void shiftSettingCheck() {
30 | for (Module m : this) {
31 | if (MouseHandler.mouseWheelMovement != 0 && Keyboard.isKeyDown(42) && !Keyboard
32 | .isKeyDown(29)) {
33 | m.mouseWheelShift -= MouseHandler.mouseWheelMovement;
34 | if (m.mouseWheelShift < 1) {
35 | m.mouseWheelShift = 1;
36 | }
37 | }
38 | }
39 | }
40 |
41 | public void movementCheck() {
42 | if (Mouse.isButtonDown(0)) {
43 | if (movingModule == null) {
44 | for (Module m : this) {
45 | if (m.inMovementBox()) {
46 | m.needsToMove = true;
47 | }
48 | }
49 | for (Module m : this) {
50 | if (movingModule != null) {
51 | m.needsToMove = false;
52 | } else if (m.needsToMove) {
53 | movingModule = m;
54 | }
55 | }
56 | }
57 | if (movingModule != null) {
58 | movingModule.handleMovement();
59 | }
60 | } else {
61 | if (movingModule != null) {
62 | movingModule.needsToMove = false;
63 | movingModule.moving = false;
64 | }
65 | movingModule = null;
66 | }
67 | }
68 |
69 | public void pageFlipCheck() {
70 | if (!Keyboard.isKeyDown(29) && !Keyboard.isKeyDown(42)) {
71 | if (MouseHandler.mouseWheelMovement != 0) {
72 | for (Module m : this) {
73 | if (m.inMovementBox() && m.getMaxShift() > 0) {
74 | m.shift += MouseHandler.mouseWheelMovement;
75 | if (m.shift > m.getMaxShift()) {
76 | m.shift = m.getMaxShift();
77 | } else if (m.shift < 0) {
78 | m.shift = 0;
79 | }
80 |
81 | break;
82 | }
83 | }
84 | }
85 | }
86 | }
87 | public void drawAllGui(){
88 | if (BazaarNotifier.config.enabled && BazaarNotifier.activeBazaar) {
89 | for (Module m : this) {
90 | if (m.isEnabled() && BazaarNotifier.inBazaar) {
91 | m.position.setSize(m.getModuleWidth(), m.getModuleHeight());
92 | m.draw();
93 | }
94 | }
95 | }
96 | }
97 |
98 |
99 | public void resetAll() {
100 | for (Module m : this) {
101 | m.reset();
102 | }
103 | }
104 |
105 | public void resetScale() {
106 | for (Module m : this) {
107 | m.setScale(1, false);
108 | }
109 | }
110 |
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/ModuleName.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules;
2 |
3 | import dev.meyi.bn.modules.module.BankModule;
4 | import dev.meyi.bn.modules.module.CraftingModule;
5 | import dev.meyi.bn.modules.module.NotificationModule;
6 | import dev.meyi.bn.modules.module.SuggestionModule;
7 |
8 | public enum ModuleName {
9 | SUGGESTION, BANK, NOTIFICATION, CRAFTING;
10 |
11 | public Module returnDefaultModule() {
12 | switch (this) {
13 | case SUGGESTION:
14 | return new SuggestionModule();
15 | case BANK:
16 | return new BankModule();
17 | case NOTIFICATION:
18 | return new NotificationModule();
19 | case CRAFTING:
20 | return new CraftingModule();
21 | }
22 | return null;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/calc/BankCalculator.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules.calc;
2 |
3 | import dev.meyi.bn.BazaarNotifier;
4 | import dev.meyi.bn.json.Exchange;
5 | import dev.meyi.bn.json.Order;
6 | import dev.meyi.bn.json.Order.OrderType;
7 | import dev.meyi.bn.utilities.Utils;
8 | import java.util.ArrayList;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 | import java.util.Map.Entry;
13 | import java.util.regex.Matcher;
14 | import java.util.regex.Pattern;
15 |
16 |
17 | public class BankCalculator {
18 |
19 | private static final List orderHistory = new ArrayList<>();
20 | private static final Pattern sellOffer = Pattern.compile(
21 | "\\[Bazaar] Claimed .* coins from selling (.*)x (.*) at (.*) each!");
22 | private static final Pattern buyOrder = Pattern.compile(
23 | "\\[Bazaar] Claimed (.*)x (.*) worth .* coins bought for (.*) each!");
24 | private static final Pattern instantSell = Pattern.compile(
25 | "\\[Bazaar] Sold (.*)x (.*) for (.*) coins!");
26 | private static final Pattern instantBuy = Pattern.compile(
27 | "\\[Bazaar] Bought (.*)x (.*) for (.*) coins!");
28 | private static double rawDifference = 0;
29 | public static double getRawDifference() {
30 | return rawDifference;
31 | }
32 |
33 | public static void calculateBazaarProfit() {
34 | for (int i = orderHistory.size() - 1; i >= 0; i--) {
35 | Exchange sell = orderHistory.get(i);
36 | if (sell.getAmount() != 0 && sell.getType() == OrderType.SELL) {
37 | for (int j = orderHistory.size() - 1; j >= 0; j--) {
38 | Exchange buy = orderHistory.get(j);
39 | if (buy.getAmount() != 0 && sell.matchesOrder(buy)) {
40 | if (buy.getAmount() >= sell.getAmount()) {
41 | BazaarNotifier.config.bankModule.bazaarProfit +=
42 | sell.getAmount() * (sell.getPricePerUnit() * .99 - buy.getPricePerUnit());
43 | buy.removeAmount(sell.getAmount());
44 | sell.removeAmount(sell.getAmount());
45 | } else {
46 | BazaarNotifier.config.bankModule.bazaarProfit +=
47 | buy.getAmount() * (sell.getPricePerUnit() * .99 - buy.getPricePerUnit());
48 | sell.removeAmount(buy.getAmount());
49 | buy.removeAmount(buy.getAmount());
50 | }
51 |
52 | if (sell.getAmount() == 0) {
53 | break;
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
60 | orderHistory.removeIf(v -> v.getAmount() == 0);
61 |
62 | craftingLoop:
63 | for (int i = orderHistory.size() - 1; i >= 0; i--) {
64 | if (orderHistory.get(i).getAmount() != 0 && orderHistory.get(i).canCraft()) {
65 | Map craftingResources = orderHistory.get(i).getCraftingResources();
66 | Map> availableResources = new HashMap<>();
67 | craftingResources.keySet().forEach(key -> availableResources.put(key, new ArrayList<>()));
68 | for (Exchange exchange : orderHistory) {
69 | if (exchange.getType() == OrderType.BUY && craftingResources.containsKey(
70 | exchange.getProductId())) {
71 | availableResources.get(exchange.getProductId()).add(exchange);
72 | }
73 | }
74 |
75 | int maxCrafting = orderHistory.get(i).getAmount();
76 |
77 | for (Entry> entry : availableResources.entrySet()) {
78 | int amountAvailable = 0;
79 | for (Exchange e : entry.getValue()) {
80 | amountAvailable += e.getAmount();
81 | }
82 |
83 | int craftCost = craftingResources.get(entry.getKey());
84 |
85 | if (amountAvailable < craftCost) {
86 | break craftingLoop;
87 | } else {
88 | maxCrafting = Math.min(maxCrafting, amountAvailable / craftCost);
89 | }
90 | }
91 |
92 | double buyValue = 0;
93 |
94 | if (maxCrafting > 0) {
95 | for (String key : availableResources.keySet()) {
96 | int valueToRemove = maxCrafting * craftingResources.get(key);
97 | for (Exchange e : availableResources.get(key)) {
98 | if (e.getAmount() >= valueToRemove) {
99 | buyValue += valueToRemove * e.getPricePerUnit();
100 | e.removeAmount(valueToRemove);
101 | valueToRemove = 0;
102 | } else {
103 | buyValue += e.getAmount() * e.getPricePerUnit();
104 | valueToRemove -= e.getAmount();
105 | e.removeAmount(e.getAmount());
106 | }
107 | }
108 | }
109 | }
110 | orderHistory.get(i).removeAmount(maxCrafting);
111 | BazaarNotifier.config.bankModule.bazaarProfit +=
112 | ((double) maxCrafting * orderHistory.get(i).getPricePerUnit()) * .99 - buyValue;
113 | }
114 | }
115 |
116 | orderHistory.removeIf(v -> v.getAmount() == 0);
117 | }
118 |
119 | public static void evaluate(String message) {
120 | String productId;
121 | double pricePerUnit;
122 | int amount;
123 | OrderType type;
124 |
125 | Matcher m;
126 |
127 | if ((m = sellOffer.matcher(message)).find()) {
128 | type = OrderType.SELL;
129 | amount = Integer.parseInt(m.group(1).replaceAll("[,.]", ""));
130 | pricePerUnit = Double.parseDouble(m.group(3).replaceAll(",", ""));
131 | } else if ((m = instantSell.matcher(message)).find()) {
132 | type = OrderType.SELL;
133 | amount = Integer.parseInt(m.group(1).replaceAll("[,.]", ""));
134 | double coins = Double.parseDouble(m.group(3).replaceAll(",", ""));
135 | pricePerUnit = coins / (double) amount;
136 | } else if ((m = buyOrder.matcher(message)).find()) {
137 | type = OrderType.BUY;
138 | amount = Integer.parseInt(m.group(1).replaceAll("[,.]", ""));
139 | pricePerUnit = Double.parseDouble(m.group(3).replaceAll(",", ""));
140 | } else if ((m = instantBuy.matcher(message)).find()) {
141 | type = OrderType.BUY;
142 | amount = Integer.parseInt(m.group(1).replaceAll("[,.]", ""));
143 | double coins = Double.parseDouble(m.group(3).replaceAll(",", ""));
144 | pricePerUnit = coins / (double) amount;
145 | } else {
146 | return;
147 | }
148 |
149 | if (BazaarNotifier.bazaarConv.containsValue(m.group(2))) {
150 | productId = BazaarNotifier.bazaarConv.inverse().get(m.group(2));
151 | } else {
152 | productId = Utils.getItemIdFromName(m.group(2))[1];
153 | }
154 |
155 | if (!productId.isEmpty()) {
156 | Exchange e = new Exchange(type, productId, pricePerUnit, amount);
157 |
158 | int index;
159 | if ((index = orderHistory.indexOf(e)) != -1) {
160 | orderHistory.get(index).addAmount(amount);
161 | } else {
162 | orderHistory.add(e);
163 | }
164 |
165 | rawDifference += (((e.getType() == OrderType.BUY) ? -1.0 : 0.99) * e.getPricePerUnit() * (double)e.getAmount());
166 | // Regardless of type, because reverse flipping is possible
167 | // aka selling an item you already own and buying back cheaper
168 | calculateBazaarProfit();
169 | }
170 | }
171 |
172 | public static void evaluateCapHit(Order order) {
173 | BazaarNotifier.config.bankModule.bazaarDailyAmount -= order.orderValue;
174 | }
175 |
176 | public static synchronized void reset() {
177 | BazaarNotifier.config.bankModule.bazaarProfit = 0;
178 | orderHistory.clear();
179 | rawDifference = 0;
180 | }
181 | }
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/calc/CraftingCalculator.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules.calc;
2 |
3 | import com.google.gson.JsonArray;
4 | import com.google.gson.JsonElement;
5 | import dev.meyi.bn.BazaarNotifier;
6 | import dev.meyi.bn.modules.module.CraftingModule;
7 | import dev.meyi.bn.utilities.Utils;
8 | import java.io.IOException;
9 | import java.util.ArrayList;
10 | import java.util.Arrays;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 |
16 | public class CraftingCalculator {
17 |
18 | private static List unlockedRecipes = new ArrayList<>();
19 |
20 |
21 | public static void getBestEnchantRecipes() {
22 | ArrayList list = new ArrayList<>();
23 | if (BazaarNotifier.enchantCraftingList == null
24 | || BazaarNotifier.bazaarDataRaw.products.size() == 0) {
25 | return;
26 | }
27 | for (Map.Entry keys : BazaarNotifier.enchantCraftingList
28 | .getAsJsonObject("other").entrySet()) {
29 |
30 | String itemName = keys.getKey();
31 | String collection = BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
32 | .getAsJsonObject(itemName)
33 | .get("collection").getAsString();
34 |
35 | if (BazaarNotifier.config.collectionCheck && !(unlockedRecipes.contains(collection)
36 | || collection.equalsIgnoreCase("NONE"))) {
37 | continue;
38 | }
39 |
40 | String[] itemCrafting = getEnchantCraft(itemName);
41 |
42 | if (!itemCrafting[6].equalsIgnoreCase("0")) {
43 | list.add(itemCrafting);
44 | }
45 | }
46 | CraftingModule.list = list;
47 |
48 | sort();
49 | }
50 |
51 |
52 | public static void toggleCrafting() {
53 | BazaarNotifier.config.craftingModule.craftingSortingOption =
54 | (BazaarNotifier.config.craftingModule.craftingSortingOption + 1) % 3;
55 | }
56 |
57 | public static String[] getEnchantCraft(String itemName) {
58 | String[] values = new String[7];
59 | Arrays.fill(values, "0");
60 |
61 | if (BazaarNotifier.enchantCraftingList.getAsJsonObject("other").has(itemName)) {
62 | if (BazaarNotifier.bazaarDataRaw.products.size() != 0) {
63 | if (BazaarNotifier.bazaarDataRaw.products.get(itemName).buy_summary.size() == 0
64 | || BazaarNotifier.bazaarDataRaw.products.get(itemName).sell_summary.size() == 0) {
65 | return values;
66 | }
67 |
68 | // buy order / instant sell
69 | double itemSellPrice = BazaarNotifier.bazaarDataRaw.products.get(itemName).sell_summary
70 | .get(0).getPriceWithTax();
71 |
72 | // instant buy / sell offer
73 | double itemBuyPrice = BazaarNotifier.bazaarDataRaw.products.get(itemName).buy_summary
74 | .get(0).getPriceWithTax();
75 | double ingredientPrice = 0d;
76 | int ingredientCount;
77 | double materialCost = 0d;
78 | double ingredientPrice2 = 0d;
79 | int ingredientCount2;
80 | double materialCost2 = 0d;
81 |
82 | boolean missingMaterialOrder = false;
83 | boolean missingMaterialInstant = false;
84 |
85 | //Buy orders
86 | for (int h = 0; h < BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
87 | .getAsJsonObject(itemName).getAsJsonArray("material").size(); h++) {
88 | if (h % 2 == 0) {
89 | if (BazaarNotifier.bazaarDataRaw.products.get(
90 | BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
91 | .getAsJsonObject(itemName)
92 | .getAsJsonArray("material").get(h).getAsString()).sell_summary.size() == 0) {
93 | missingMaterialOrder = true;
94 | break;
95 | }
96 | ingredientPrice = BazaarNotifier.bazaarDataRaw.products.get(
97 | BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
98 | .getAsJsonObject(itemName).getAsJsonArray("material").get(h).getAsString())
99 | .sell_summary.get(0).pricePerUnit;
100 | } else {
101 | ingredientCount = BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
102 | .getAsJsonObject(itemName).getAsJsonArray("material").get(h).getAsInt();
103 | materialCost += (ingredientPrice * ingredientCount);
104 | }
105 | }
106 |
107 | //Instant buy
108 | for (int h = 0; h < BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
109 | .getAsJsonObject(itemName).getAsJsonArray("material").size(); h++) {
110 | if (h % 2 == 0) {
111 | if (BazaarNotifier.bazaarDataRaw.products.get(
112 | BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
113 | .getAsJsonObject(itemName)
114 | .getAsJsonArray("material").get(h).getAsString()).buy_summary.size() == 0) {
115 | missingMaterialInstant = true;
116 | break;
117 | }
118 | ingredientPrice2 = BazaarNotifier.bazaarDataRaw.products.get(
119 | BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
120 | .getAsJsonObject(itemName).getAsJsonArray("material").get(h).getAsString())
121 | .buy_summary.get(0).pricePerUnit;
122 | } else {
123 | ingredientCount2 = BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
124 | .getAsJsonObject(itemName).getAsJsonArray("material").get(h).getAsInt();
125 | materialCost2 += (ingredientPrice2 * ingredientCount2);
126 | }
127 | }
128 |
129 | if (!missingMaterialOrder) {
130 | double profitInstaSell = itemSellPrice - materialCost;
131 | double profitSellOffer = itemBuyPrice - materialCost;
132 | double profitPercentage = (itemSellPrice / materialCost - 1) * 100;
133 | values[0] = String.valueOf(profitInstaSell);
134 | values[1] = String.valueOf(profitSellOffer);
135 | values[2] = String.valueOf(profitPercentage);
136 | }
137 |
138 | if (!missingMaterialInstant) {
139 | double profitInstaSell2 = itemSellPrice - materialCost2;
140 | double profitSellOffer2 = itemBuyPrice - materialCost2;
141 | double profitPercentage2 = (itemSellPrice / materialCost2 - 1) * 100;
142 | values[3] = String.valueOf(profitInstaSell2);
143 | values[4] = String.valueOf(profitSellOffer2);
144 | values[5] = String.valueOf(profitPercentage2);
145 | }
146 | }
147 | values[6] = itemName;
148 | }
149 |
150 | return values;
151 |
152 | }
153 |
154 | public static Map getMaterialsMap(String productId) {
155 | JsonArray materialsArray = BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
156 | .getAsJsonObject(productId).getAsJsonArray("material");
157 | Map materials = new HashMap<>();
158 | for (int i = 0; i < materialsArray.size(); i += 2) {
159 | materials.put(materialsArray.get(i).getAsString(), materialsArray.get(i + 1).getAsInt());
160 | }
161 | return materials;
162 | }
163 |
164 | public static void getUnlockedRecipes() {
165 | try {
166 | List s = Utils.unlockedRecipes();
167 | if (s != null) {
168 | unlockedRecipes = s;
169 |
170 | // Honestly, if this is empty, we should just assume something went wrong and disable the collection check.
171 | if (unlockedRecipes.size() == 0) {
172 | BazaarNotifier.config.collectionCheck = false;
173 | }
174 | } else {
175 | BazaarNotifier.config.collectionCheck = false;
176 | }
177 | } catch (IOException e) {
178 | e.printStackTrace();
179 | }
180 | }
181 |
182 | public static void sort() {
183 | int i = BazaarNotifier.config.craftingModule.useBuyOrders ? 0 : 3;
184 |
185 | CraftingModule.list.sort((o1, o2) -> Double.compare(
186 | Double.parseDouble(o2[i + BazaarNotifier.config.craftingModule.craftingSortingOption]),
187 | Double.parseDouble(o1[i + BazaarNotifier.config.craftingModule.craftingSortingOption])
188 | ));
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/calc/SuggestionCalculator.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules.calc;
2 |
3 |
4 | import dev.meyi.bn.BazaarNotifier;
5 | import dev.meyi.bn.json.resp.BazaarItem;
6 | import dev.meyi.bn.modules.module.SuggestionModule;
7 | import java.util.Collections;
8 | import java.util.Comparator;
9 | import java.util.LinkedList;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 |
14 | public class SuggestionCalculator {
15 |
16 | public static void basic() {
17 | try {
18 | List list = new LinkedList<>();
19 | for (Map.Entry entry : BazaarNotifier.bazaarDataRaw.products
20 | .entrySet()) {
21 | String key = entry.getKey();
22 | BazaarItem product = BazaarNotifier.bazaarDataRaw.products.get(key);
23 |
24 | if (!BazaarNotifier.config.suggestionModule.suggestionShowEnchantments &&
25 | key.startsWith("ENCHANTMENT")) {
26 | continue;
27 | }
28 |
29 | if (!BazaarNotifier.bazaarConv.containsKey(key)) {
30 | BazaarNotifier.bazaarConv.put(key, key);
31 | }
32 | String productId = BazaarNotifier.bazaarConv.get(key);
33 |
34 | list.add(new String[]{productId, Double.toString(calculateEP(product))});
35 | }
36 | list.sort(Comparator.comparingDouble(o -> Double.parseDouble(o[1])));
37 | Collections.reverse(list);
38 | SuggestionModule.list = list;
39 |
40 | } catch (Exception e) {
41 | e.printStackTrace();
42 | }
43 | }
44 |
45 | public static double calculateEP(BazaarItem item) {
46 | double sellPrice = (item.buy_summary.size() > 0 && item.sell_summary.size() > 0) ?
47 | item.buy_summary.get(0).pricePerUnit : 0;
48 | double buyPrice = (item.sell_summary.size() > 0 && item.buy_summary.size() > 0) ?
49 | item.sell_summary.get(0).pricePerUnit : 0;
50 | long sellMovingWeek = item.quick_status.sellMovingWeek;
51 | long buyMovingWeek = item.quick_status.buyMovingWeek;
52 | return (buyMovingWeek + sellMovingWeek == 0) ? 0 : ((buyMovingWeek * sellMovingWeek) /
53 | (10080d * (buyMovingWeek + sellMovingWeek))) * (sellPrice * .99d - buyPrice);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/module/BankModule.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules.module;
2 |
3 | import cc.polyfrost.oneconfig.config.annotations.Switch;
4 | import cc.polyfrost.oneconfig.config.migration.JsonName;
5 | import cc.polyfrost.oneconfig.libs.universal.UMatrixStack;
6 | import dev.meyi.bn.BazaarNotifier;
7 | import dev.meyi.bn.modules.Module;
8 | import dev.meyi.bn.modules.ModuleName;
9 | import dev.meyi.bn.modules.calc.BankCalculator;
10 | import dev.meyi.bn.utilities.ColoredText;
11 | import dev.meyi.bn.utilities.RenderUtils;
12 | import dev.meyi.bn.utilities.Defaults;
13 | import java.awt.Color;
14 | import java.util.ArrayList;
15 |
16 | import net.minecraft.client.Minecraft;
17 | import org.lwjgl.opengl.GL11;
18 |
19 | public class BankModule extends Module {
20 | @JsonName("bazaarProfit")
21 | public double bazaarProfit = 0;
22 |
23 | @JsonName("bazaarDailyAmount")
24 | public double bazaarDailyAmount = 1E10;
25 |
26 | public BankModule() {
27 | super();
28 | }
29 |
30 | public transient static final ModuleName type = ModuleName.BANK;
31 | transient int lines = 2;
32 |
33 | @JsonName("bankRawDifference")
34 | @Switch(name = "Raw Difference",
35 | category = "Bank Module",
36 | description = "Show profit including current orders"
37 | )
38 | public boolean bankRawDifference = Defaults.BANK_RAW_DIFFERENCE;
39 |
40 | @Override
41 | protected void draw(UMatrixStack matrices, float x, float y, float scale, boolean example) {
42 | draw();
43 | }
44 |
45 | @Override
46 | protected float getWidth(float scale, boolean example) {
47 | return RenderUtils.getStringWidth(longestString)* scale + 2 * padding * scale;
48 | }
49 |
50 | @Override
51 | protected float getHeight(float scale, boolean example) {
52 | return (((Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT * lines) + lines ) * scale - 2) + 2 * padding * scale;
53 | }
54 |
55 |
56 | @Override
57 | public void draw() {
58 | GL11.glTranslated(0, 0, 1);
59 | drawBounds();
60 | ArrayList> items = new ArrayList<>();
61 |
62 | ArrayList header = new ArrayList<>();
63 | header.add(new ColoredText("Bank Module", BazaarNotifier.config.infoColor.toJavaColor()));
64 | items.add(header);
65 |
66 | ArrayList bazaarProfitMessage = new ArrayList<>();
67 | bazaarProfitMessage.add(new ColoredText("Bazaar Profit: ", BazaarNotifier.config.itemColor.toJavaColor()));
68 | bazaarProfitMessage.add(new ColoredText(BazaarNotifier.df.format(bazaarProfit), Color.ORANGE));
69 | items.add(bazaarProfitMessage);
70 |
71 |
72 | if (BazaarNotifier.config.bankModule.bankRawDifference) {
73 | ArrayList bazaarDifferenceMessage = new ArrayList<>();
74 | bazaarDifferenceMessage.add(new ColoredText("Bazaar Difference: ", BazaarNotifier.config.itemColor.toJavaColor()));
75 | bazaarDifferenceMessage.add(new ColoredText(BazaarNotifier.df.format(BankCalculator.getRawDifference()), Color.ORANGE));
76 | items.add(bazaarDifferenceMessage);
77 | }
78 |
79 | if (BazaarNotifier.config.bankModule.bazaarDailyAmount <= 1E9) {
80 | ArrayList bazaarDifferenceMessage = new ArrayList<>();
81 | bazaarDifferenceMessage.add(new ColoredText("Daily Limit: ", BazaarNotifier.config.itemColor.toJavaColor()));
82 | if (BazaarNotifier.config.bankModule.bazaarDailyAmount > 0) {
83 | bazaarDifferenceMessage.add(new ColoredText(
84 | BazaarNotifier.df.format(Math.max(BazaarNotifier.config.bankModule.bazaarDailyAmount, 0)),
85 | Color.ORANGE));
86 | } else {
87 | bazaarDifferenceMessage.add(new ColoredText("NONE", Color.RED));
88 | }
89 | items.add(bazaarDifferenceMessage);
90 | }
91 |
92 | lines = 2 + (BazaarNotifier.config.bankModule.bankRawDifference ? 1 : 0)
93 | + (BazaarNotifier.config.bankModule.bazaarDailyAmount <= 1E9 ? 1 : 0);
94 |
95 |
96 | longestString = RenderUtils.getLongestString(items);
97 | RenderUtils.drawColorfulParagraph(items, (int)position.getX() + padding, (int)position.getY() + padding, scale);
98 | GL11.glTranslated(0, 0, -1);
99 | }
100 |
101 | @Override
102 | protected void reset() {
103 | position.setPosition(Defaults.BANK_MODULE_X, Defaults.BANK_MODULE_Y);
104 | setScale(1, false);
105 |
106 | BankCalculator.reset();
107 | enabled = true;
108 | }
109 |
110 | @Override
111 | public String name() {
112 | return ModuleName.BANK.name();
113 | }
114 |
115 | @Override
116 | protected boolean shouldDrawBounds() {
117 | return true;
118 | }
119 |
120 | @Override
121 | protected int getMaxShift() {
122 | return 0;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/module/CraftingModule.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules.module;
2 |
3 | import cc.polyfrost.oneconfig.config.annotations.Checkbox;
4 | import cc.polyfrost.oneconfig.config.annotations.Dropdown;
5 | import cc.polyfrost.oneconfig.config.annotations.DualOption;
6 | import cc.polyfrost.oneconfig.config.annotations.Slider;
7 | import cc.polyfrost.oneconfig.config.migration.JsonName;
8 | import cc.polyfrost.oneconfig.libs.universal.UMatrixStack;
9 | import dev.meyi.bn.BazaarNotifier;
10 | import dev.meyi.bn.modules.Module;
11 | import dev.meyi.bn.modules.ModuleName;
12 | import dev.meyi.bn.modules.calc.CraftingCalculator;
13 | import dev.meyi.bn.utilities.ColoredText;
14 | import dev.meyi.bn.utilities.Defaults;
15 | import dev.meyi.bn.utilities.RenderUtils;
16 | import java.awt.Color;
17 | import java.util.ArrayList;
18 | import net.minecraft.client.Minecraft;
19 | import net.minecraft.client.gui.Gui;
20 | import org.lwjgl.input.Mouse;
21 | import org.lwjgl.opengl.GL11;
22 |
23 |
24 | public class CraftingModule extends Module {
25 |
26 | public transient static final ModuleName type = ModuleName.CRAFTING;
27 | public transient static ArrayList list = new ArrayList<>();
28 | private transient static boolean mouseButtonDown;
29 | @JsonName("craftingListLength")
30 | @Slider(name = "Crafting List Entries",
31 | category = "Crafting Module",
32 | description = "The amount of entries in the Crafting Module list",
33 | min = 1, max = 25, step = 1
34 | )
35 | public int craftingListLength = Defaults.CRAFTING_LIST_LENGTH;
36 | @JsonName("craftingSortingOption")
37 | @Dropdown(name = "Crafting Sorting Option",
38 | options = {"Instant Sell", "Sell Offer", "1m instant"},
39 | category = "Crafting Module",
40 | description = "After which condition should the crafting module be sorted"
41 | )
42 | public int craftingSortingOption = Defaults.CRAFTING_SORTING_OPTION;
43 | @JsonName("useBuyOrders")
44 | @DualOption(name = "Use Buy Orders",
45 | left = "Instant Buy",
46 | right = "Buy Orders",
47 | description = "How you want to buy the crafting materials",
48 | category = "Crafting Module"
49 | )
50 | public boolean useBuyOrders = Defaults.USE_BUY_ORDERS;
51 | @Checkbox(name = "Show Instant Sell Profit",
52 | category = "Crafting Module",
53 | description = "Shows the instant sell profit tab"
54 | )
55 | public boolean showInstantSellProfit = Defaults.INSTANT_SELL_PROFIT;
56 | @Checkbox(name = "Show Sell Offer Profit",
57 | category = "Crafting Module",
58 | description = "Shows the sell offer profit tab"
59 | )
60 | public boolean showSellOfferProfit = Defaults.SELL_OFFER_PROFIT;
61 | @Checkbox(name = "Show Profit Percentage",
62 | category = "Crafting Module",
63 | description = "Shows the profit percentage tab"
64 | )
65 | public boolean showProfitPerMil = Defaults.PROFIT_PER_MIL;
66 | transient int lastHovered = 0;
67 |
68 |
69 | public CraftingModule() {
70 | super();
71 | }
72 |
73 | @Override
74 | protected void draw(UMatrixStack matrices, float x, float y, float scale, boolean example) {
75 | draw();
76 | }
77 |
78 | @Override
79 | protected float getWidth(float scale, boolean example) {
80 | return RenderUtils.getStringWidth(longestString) * scale + 2 * padding * scale;
81 | }
82 |
83 | @Override
84 | protected float getHeight(float scale, boolean example) {
85 | try {
86 | return ((Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT
87 | * (BazaarNotifier.config.craftingModule.craftingListLength + 1)
88 | + (BazaarNotifier.config.craftingModule.craftingListLength + 1) * 2) * scale - 2)
89 | + 2 * scale * padding;
90 | } catch (NullPointerException e) {
91 | return 1;
92 | }
93 | }
94 |
95 |
96 | private ArrayList generateHelperLine() {
97 | if (Mouse.isButtonDown(1) && getMouseCoordinateY() > position.getY() - 1
98 | && getMouseCoordinateY()
99 | < position.getY() + (Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT + 1) * scale) {
100 | int[] width = new int[4];
101 | int totalWidth = 0;
102 | int relativeX = getMouseCoordinateX() - (int) position.getX();
103 | width[0] =
104 | (int) (Minecraft.getMinecraft().fontRendererObj.getStringWidth(
105 | BazaarNotifier.config.craftingModule.useBuyOrders ? " Profits (Buy Orders) -"
106 | : " Profits (Instant Buy) -") * scale);
107 | width[1] = BazaarNotifier.config.craftingModule.showInstantSellProfit ?
108 | (int) (Minecraft.getMinecraft().fontRendererObj.getStringWidth(" Instant Sell ") * scale)
109 | : 0;
110 | width[2] = BazaarNotifier.config.craftingModule.showSellOfferProfit ?
111 | (int) (Minecraft.getMinecraft().fontRendererObj.getStringWidth("/ Sell Offer ") * scale)
112 | : 0;
113 | width[3] = BazaarNotifier.config.craftingModule.showProfitPerMil ?
114 | (int) (Minecraft.getMinecraft().fontRendererObj.getStringWidth("/ Profit %") * scale)
115 | : 0;
116 |
117 | for (int i : width) {
118 | totalWidth += i;
119 | }
120 |
121 | for (int i = 3; i >= 0; i--) {
122 | if (totalWidth > relativeX && totalWidth - width[i] < relativeX && inMovementBox()
123 | && BazaarNotifier.inBazaar) {
124 | switch (i) {
125 | case 0:
126 | if (mouseButtonDown) {
127 | BazaarNotifier.config.craftingModule.useBuyOrders ^= true;
128 | mouseButtonDown = false;
129 | }
130 | break;
131 | case 1:
132 | BazaarNotifier.config.craftingModule.craftingSortingOption = 0;
133 | break;
134 | case 2:
135 | BazaarNotifier.config.craftingModule.craftingSortingOption = 1;
136 | break;
137 | case 3:
138 | BazaarNotifier.config.craftingModule.craftingSortingOption = 2;
139 | break;
140 | }
141 | CraftingCalculator.sort();
142 | break;
143 | }
144 | totalWidth -= width[i];
145 | }
146 | } else if (!Mouse.isButtonDown(1)) {
147 | mouseButtonDown = true;
148 | }
149 | ArrayList helperLine = new ArrayList<>();
150 | helperLine.add(new ColoredText(" ", BazaarNotifier.config.infoColor.toJavaColor()));
151 | helperLine
152 | .add(new ColoredText(
153 | BazaarNotifier.config.craftingModule.useBuyOrders ? "Profits (Buy Orders)"
154 | : "Profits (Instant Buy)",
155 | BazaarNotifier.config.infoColor.toJavaColor()));
156 | if (BazaarNotifier.config.craftingModule.showProfitPerMil || BazaarNotifier.config
157 | .craftingModule.showInstantSellProfit
158 | || BazaarNotifier.config.craftingModule.showSellOfferProfit) {
159 | helperLine.add(new ColoredText(" - ", BazaarNotifier.config.infoColor.toJavaColor()));
160 | }
161 | if (BazaarNotifier.config.craftingModule.showInstantSellProfit) {
162 | helperLine.add(new ColoredText(" Instant Sell",
163 | BazaarNotifier.config.craftingModule.craftingSortingOption == 0 ?
164 | new Color(141, 152, 201) : BazaarNotifier.config.infoColor.toJavaColor()));
165 | if (BazaarNotifier.config.craftingModule.showSellOfferProfit
166 | || BazaarNotifier.config.craftingModule.showProfitPerMil) {
167 | helperLine.add(new ColoredText(" /", BazaarNotifier.config.infoColor.toJavaColor()));
168 | }
169 | }
170 | if (BazaarNotifier.config.craftingModule.showSellOfferProfit) {
171 | helperLine.add(new ColoredText(" Sell Offer",
172 | BazaarNotifier.config.craftingModule.craftingSortingOption == 1 ?
173 | new Color(141, 152, 201) : BazaarNotifier.config.infoColor.toJavaColor()));
174 | if (BazaarNotifier.config.craftingModule.showProfitPerMil) {
175 | helperLine.add(new ColoredText(" /", BazaarNotifier.config.infoColor.toJavaColor()));
176 | }
177 | }
178 | if (BazaarNotifier.config.craftingModule.showProfitPerMil) {
179 | helperLine.add(new ColoredText(" Profit %",
180 | BazaarNotifier.config.craftingModule.craftingSortingOption == 2 ?
181 | new Color(141, 152, 201) : BazaarNotifier.config.infoColor.toJavaColor()));
182 | }
183 |
184 | return helperLine;
185 | }
186 |
187 | @Override
188 | public void draw() {
189 | GL11.glTranslated(0, 0, 1);
190 | drawBounds();
191 | if (BazaarNotifier.bazaarDataRaw != null) {
192 | ArrayList> items = new ArrayList<>();
193 | items.add(generateHelperLine());
194 | for (int i = shift; i < BazaarNotifier.config.craftingModule.craftingListLength + shift;
195 | i++) {
196 | ArrayList message = new ArrayList<>();
197 | if (i < list.size()) {
198 | if (list.get(i).length != 0) {
199 | Double profitInstaSell = Double.valueOf(list.get(i)[BazaarNotifier.config.craftingModule.useBuyOrders ? 0 : 3]);
200 | Double profitSellOffer = Double.valueOf(list.get(i)[BazaarNotifier.config.craftingModule.useBuyOrders ? 1 : 4]);
201 | Double profitPercentage = Double.valueOf(list.get(i)[BazaarNotifier.config.craftingModule.useBuyOrders ? 2 : 5]);
202 | String itemName = list.get(i)[6];
203 |
204 | String itemNameConverted = BazaarNotifier.bazaarConv.get(itemName);
205 | message.add(new ColoredText(String.valueOf(i + 1),
206 | BazaarNotifier.config.numberColor.toJavaColor()));
207 | message.add(new ColoredText(". ", BazaarNotifier.config.numberColor.toJavaColor()));
208 | message.add(
209 | new ColoredText(itemNameConverted, BazaarNotifier.config.itemColor.toJavaColor()));
210 |
211 | if (BazaarNotifier.config.craftingModule.showProfitPerMil
212 | || BazaarNotifier.config.craftingModule.showInstantSellProfit
213 | || BazaarNotifier.config.craftingModule.showSellOfferProfit) {
214 | message.add(new ColoredText(" - ", BazaarNotifier.config.infoColor.toJavaColor()));
215 | }
216 |
217 | if (BazaarNotifier.config.craftingModule.showInstantSellProfit) {
218 | message.add(new ColoredText(BazaarNotifier.df.format(profitInstaSell),
219 | getColor(profitInstaSell.intValue())));
220 |
221 | if (BazaarNotifier.config.craftingModule.showSellOfferProfit) {
222 | message.add(new ColoredText(" / ", BazaarNotifier.config.infoColor.toJavaColor()));
223 | }
224 | }
225 |
226 | if (BazaarNotifier.config.craftingModule.showSellOfferProfit) {
227 | message.add(new ColoredText(BazaarNotifier.df.format(profitSellOffer),
228 | getColor(profitSellOffer.intValue())));
229 |
230 | if (BazaarNotifier.config.craftingModule.showProfitPerMil) {
231 | message.add(new ColoredText(" / ", BazaarNotifier.config.infoColor.toJavaColor()));
232 | }
233 | }
234 |
235 | if (BazaarNotifier.config.craftingModule.showProfitPerMil) {
236 | message.add(new ColoredText(BazaarNotifier.df.format(profitPercentage) + "%",
237 | getColorForPercentage(profitPercentage.intValue())));
238 | }
239 | } else {
240 | message.add(new ColoredText("Error, just wait", Color.RED));
241 | }
242 | items.add(message);
243 | }
244 | }
245 | longestString = RenderUtils.getLongestString(items);
246 |
247 | RenderUtils.drawColorfulParagraph(items, (int) position.getX() + padding, (int) position.getY() + padding, scale);
248 | if (BazaarNotifier.inBazaar) {
249 | renderMaterials(checkHoveredText(), list);
250 | }
251 | } else {
252 | RenderUtils.drawCenteredString("Waiting for bazaar data", (int) position.getX(),
253 | (int) position.getY(), 0xAAAAAA, scale);
254 | //Todo add height and width
255 | }
256 | GL11.glTranslated(0, 0, -1);
257 | }
258 |
259 | protected Color getColor(int price) {
260 | if (price <= 0) {
261 | return Color.RED;
262 | } else if (price <= 5000) {
263 | return Color.YELLOW;
264 | } else {
265 | return Color.GREEN;
266 | }
267 | }
268 |
269 | protected Color getColorForPercentage(int price) {
270 | if (price <= 0) {
271 | return Color.RED;
272 | } else if (price <= 10) {
273 | return Color.YELLOW;
274 | } else {
275 | return Color.GREEN;
276 | }
277 | }
278 |
279 | @Override
280 | protected void reset() {
281 | position.setPosition(Defaults.CRAFTING_MODULE_X, Defaults.CRAFTING_MODULE_Y);
282 | setScale(1, false);
283 | enabled = true;
284 | BazaarNotifier.config.craftingModule.craftingListLength = Defaults.CRAFTING_LIST_LENGTH;
285 | }
286 |
287 | @Override
288 | public String name() {
289 | return ModuleName.CRAFTING.name();
290 | }
291 |
292 | @Override
293 | protected boolean shouldDrawBounds() {
294 | return true;
295 | }
296 |
297 | @Override
298 | protected int getMaxShift() {
299 | return list.size() - BazaarNotifier.config.craftingModule.craftingListLength;
300 | }
301 |
302 | protected int checkHoveredText() {
303 | float _y = position.getY() +(-padding + 11) * scale;
304 | float y2 = _y + ((BazaarNotifier.config.craftingModule.craftingListLength) * 11 * scale);
305 | int mouseYFormatted = getMouseCoordinateY();
306 | float relativeYMouse = (mouseYFormatted - _y) / (11 * scale);
307 | if (getWidth(scale, false) != 0) {
308 | if (inMovementBox() && mouseYFormatted >= _y && mouseYFormatted <= y2) {
309 | return (int) relativeYMouse + shift;
310 | } else {
311 | return -1;
312 | }
313 | } else {
314 | return 1;
315 | }
316 | }
317 |
318 | protected void renderMaterials(int hoveredText, ArrayList list) {
319 | checkMouseMovement();
320 | ArrayList> material = new ArrayList<>();
321 | ArrayList text = new ArrayList<>();
322 |
323 | if (hoveredText > -1) {
324 | if (hoveredText < list.size()) {
325 |
326 | int materialCount;
327 | materialCount = BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
328 | .getAsJsonObject(list.get(hoveredText)[6]).getAsJsonArray("material").size();
329 | for (int b = 0; b < materialCount / 2; b++) {
330 | StringBuilder _material = new StringBuilder();
331 | if (b == 0) {
332 | _material.append((BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
333 | .getAsJsonObject(list.get(hoveredText)[6]).getAsJsonArray("material").get(1)
334 | .getAsInt()
335 | * mouseWheelShift)).append("x ").append(BazaarNotifier.bazaarConv
336 | .get(BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
337 | .getAsJsonObject(list.get(hoveredText)[6]).getAsJsonArray("material")
338 | .get(0).getAsString()));
339 | } else {
340 | _material.append(" | ").append(
341 | BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
342 | .getAsJsonObject(list.get(hoveredText)[6]).getAsJsonArray("material")
343 | .get(b * 2 + 1).getAsInt() * mouseWheelShift).append("x ").append(
344 | BazaarNotifier.bazaarConv.get(
345 | BazaarNotifier.enchantCraftingList.getAsJsonObject("other")
346 | .getAsJsonObject(list.get(hoveredText)[6]).getAsJsonArray("material")
347 | .get(b * 2).getAsString()));
348 | }
349 |
350 | text.add(new ColoredText(_material.toString(), Color.LIGHT_GRAY));
351 | }
352 | material.add(text);
353 | int longestXString = RenderUtils.drawColorfulParagraph(material, getMouseCoordinateX(),
354 | getMouseCoordinateY() - (int) (8 * scale), scale);
355 | Gui.drawRect(getMouseCoordinateX() - padding,
356 | getMouseCoordinateY() - (int) (8 * scale) - (int) (padding * scale),
357 | (int) (getMouseCoordinateX() + longestXString + padding * scale),
358 | (int) (getMouseCoordinateY() + padding * scale), 0xFF404040);
359 | RenderUtils.drawColorfulParagraph(material, getMouseCoordinateX(),
360 | getMouseCoordinateY() - (int) (8 * scale), scale);
361 | }
362 | }
363 | }
364 |
365 |
366 | public void checkMouseMovement() {
367 | if (lastHovered != checkHoveredText()) {
368 | mouseWheelShift = 1;
369 | }
370 | lastHovered = checkHoveredText();
371 | }
372 | }
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/module/NotificationModule.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules.module;
2 |
3 | import cc.polyfrost.oneconfig.libs.universal.UMatrixStack;
4 | import dev.meyi.bn.BazaarNotifier;
5 | import dev.meyi.bn.json.Order;
6 | import dev.meyi.bn.modules.Module;
7 | import dev.meyi.bn.modules.ModuleName;
8 | import dev.meyi.bn.utilities.ColoredText;
9 | import dev.meyi.bn.utilities.Defaults;
10 | import dev.meyi.bn.utilities.ReflectionHelper;
11 | import dev.meyi.bn.utilities.RenderUtils;
12 | import dev.meyi.bn.utilities.Utils;
13 | import java.awt.Color;
14 | import java.util.ArrayList;
15 | import java.util.List;
16 | import net.minecraft.client.Minecraft;
17 | import net.minecraft.client.gui.Gui;
18 | import net.minecraft.client.gui.ScaledResolution;
19 | import net.minecraft.client.gui.inventory.GuiChest;
20 | import net.minecraft.inventory.IInventory;
21 | import net.minecraft.item.Item;
22 | import net.minecraft.item.ItemStack;
23 | import net.minecraft.util.StringUtils;
24 | import org.apache.commons.lang3.text.WordUtils;
25 | import org.lwjgl.opengl.GL11;
26 |
27 | public class NotificationModule extends Module {
28 |
29 | public transient static final ModuleName type = ModuleName.NOTIFICATION;
30 |
31 | public NotificationModule() {
32 | super();
33 | }
34 |
35 | @Override
36 | protected void draw(UMatrixStack matrices, float x, float y, float scale, boolean example) {
37 | draw();
38 | }
39 |
40 | @Override
41 | protected float getWidth(float scale, boolean example) {
42 | if (longestString != null) {
43 | if (!longestString.isEmpty()) {
44 | return RenderUtils.getStringWidth(longestString) * scale + 2 * padding * scale;
45 | }
46 | }
47 | return 200*scale;
48 | }
49 |
50 | @Override
51 | protected float getHeight(float scale, boolean example) {
52 | return ((Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT * 10 + 20) * scale - 2) + 2 * padding * scale;
53 | }
54 | //source dsm
55 | public static void drawOnSlot(int chestSize, int slot, int color) {
56 | chestSize += 36;
57 | ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft());
58 | int guiLeft = (sr.getScaledWidth() - 176) / 2;
59 | int guiTop = (sr.getScaledHeight() - 222) / 2;
60 | int xSlotPos = (slot % 9) * 18 + 8;
61 | int ySlotPos = slot / 9;
62 | ySlotPos = ySlotPos * 18 + 18;
63 | int x = guiLeft + xSlotPos;
64 | int y = guiTop + ySlotPos;
65 | // Move down when chest isn't 6 rows
66 | if (chestSize != 90) {
67 | y += (6 - (chestSize - 36) / 9) * 9;
68 | }
69 | GL11.glTranslated(0, 0, 1);
70 | Gui.drawRect(x, y, x + 16, y + 16, color);
71 | GL11.glTranslated(0, 0, -1);
72 | }
73 |
74 | @Override
75 | public void draw() {
76 | GL11.glTranslated(0, 0, 1);
77 | // add extra space after "Buy" so it lines up with sell
78 | drawBounds();
79 | ArrayList> items = new ArrayList<>();
80 |
81 | if (BazaarNotifier.orders.size() != 0) {
82 |
83 | int size = Math.min(shift + 10, BazaarNotifier.orders.size());
84 |
85 | for (int i = shift; i < size; i++) {
86 | Order currentOrder = BazaarNotifier.orders.get(i);
87 | ArrayList message = new ArrayList<>();
88 |
89 | Color statusSpecificColor = currentOrder.orderStatus == Order.OrderStatus.BEST
90 | || currentOrder.orderStatus == Order.OrderStatus.SEARCHING
91 | ? Color.GREEN : currentOrder.orderStatus == Order.OrderStatus.MATCHED?
92 | Color.YELLOW : Color.RED;
93 | Color typeSpecificColor = currentOrder.type == Order.OrderType.BUY?new Color( 90, 0, 250):Color.CYAN;
94 |
95 | message.add(new ColoredText(i+1 + ". ", BazaarNotifier.config.numberColor.toJavaColor()));
96 | message.add(new ColoredText(WordUtils.capitalizeFully(currentOrder.type.name()),typeSpecificColor));
97 | message.add(new ColoredText(" - ", BazaarNotifier.config.infoColor.toJavaColor()));
98 | message.add(new ColoredText(BazaarNotifier.dfNoDecimal.format(currentOrder.startAmount)+ "x ",
99 | BazaarNotifier.config.itemColor.toJavaColor()));
100 | message.add(new ColoredText(currentOrder.product , BazaarNotifier.config.itemColor.toJavaColor()));
101 | message.add(new ColoredText(" - ", BazaarNotifier.config.infoColor.toJavaColor()));
102 | message.add(new ColoredText(currentOrder.orderStatus.name() + " ", statusSpecificColor));
103 |
104 |
105 | items.add(message);
106 | }
107 | longestString = RenderUtils.getLongestString(items);
108 | RenderUtils.drawColorfulParagraph(items, (int)position.getX() + padding, (int)position.getY() + padding, scale);
109 | } else {
110 | longestString = "";
111 | RenderUtils.drawCenteredString("No orders found", (int)position.getX(), (int)position.getY(), 0xAAAAAA, scale);
112 | }
113 | highlightOrder(checkHoveredText());
114 | GL11.glTranslated(0, 0, -1);
115 | }
116 |
117 | @Override
118 | protected void reset() {
119 | position.setPosition(Defaults.NOTIFICATION_MODULE_X, Defaults.NOTIFICATION_MODULE_Y);
120 | setScale(1, false);
121 | enabled = true;
122 | }
123 |
124 | @Override
125 | public String name() {
126 | return ModuleName.NOTIFICATION.name();
127 | }
128 |
129 | @Override
130 | protected boolean shouldDrawBounds() {
131 | return true;
132 | }
133 |
134 | @Override
135 | protected int getMaxShift() {
136 | return BazaarNotifier.orders.size() - 10;
137 | }
138 |
139 | protected int checkHoveredText() {
140 | float _y = position.getY();
141 | float y2 = _y + (10 * 11 * scale);
142 | int mouseYFormatted = getMouseCoordinateY();
143 | int mouseXFormatted = getMouseCoordinateX();
144 | float relativeYMouse = (mouseYFormatted - _y) / (11 * scale);
145 | if (this.getWidth(scale, false) != 0) {
146 | if (inMovementBox() && mouseYFormatted >= _y && mouseYFormatted <= y2 - 3 * scale) {
147 | return Math.round(relativeYMouse + shift);
148 | } else {
149 | return -1;
150 | }
151 | } else {
152 | return -1;
153 | }
154 | }
155 |
156 | public void highlightOrder(int hoveredText) {
157 | if (BazaarNotifier.orders.size() <= hoveredText || hoveredText == -1) {
158 | return;
159 | }
160 |
161 | if (Minecraft.getMinecraft().currentScreen instanceof GuiChest && BazaarNotifier.inBazaar
162 | && BazaarNotifier.activeBazaar) {
163 | IInventory chest = ReflectionHelper.getLowerChestInventory(
164 | (GuiChest) Minecraft.getMinecraft().currentScreen);
165 | if (chest == null) {
166 | return;
167 | }
168 | String chestName = chest.getDisplayName().getUnformattedText().toLowerCase();
169 |
170 | if (chestName.contains("bazaar orders")) {
171 | ItemStack[] items = new ItemStack[chest.getSizeInventory()];
172 | for (int i = 0; i < chest.getSizeInventory(); i++) {
173 | items[i] = chest.getStackInSlot(i);
174 | }
175 |
176 | for (int j = 0; j < items.length; j++) {
177 | ItemStack item = items[j];
178 |
179 | if (item == null
180 | || Item.itemRegistry.getIDForObject(item.getItem()) == 160
181 | || Item.itemRegistry.getIDForObject(item.getItem()) == 102
182 | || Item.itemRegistry.getIDForObject(item.getItem()) == 154
183 | || Item.itemRegistry.getIDForObject(item.getItem()) == 262) {
184 | continue;
185 | }
186 | String itemDisplayName = StringUtils.stripControlCodes(item.getDisplayName());
187 | Order.OrderType type = itemDisplayName.split(" ")[0]
188 | .equalsIgnoreCase("sell") ? Order.OrderType.SELL : Order.OrderType.BUY;
189 | String product = itemDisplayName
190 | .replaceAll("SELL ", "").replaceAll("BUY ", "");
191 | List lore = Utils.getLoreFromItemStack(item);
192 |
193 | int amount = Integer.parseInt(
194 | lore.get(2).toLowerCase().split("amount: ")[1].replaceAll("[x,.]", ""));
195 |
196 | String ppu = lore.get(3).equals("") ? lore.get(4) : lore.get(5);
197 | ppu = ppu.toLowerCase().replace("price per unit: ", "").replace(" coins", "")
198 | .replaceAll(",", "");
199 |
200 | if (!ppu.contains("expired!") && !ppu.contains("expires in")) {
201 |
202 | double pricePerUnit = Double.parseDouble(ppu);
203 |
204 | Order o = new Order(product, type, pricePerUnit, amount);
205 |
206 | for (int i = 0; i < BazaarNotifier.orders.size(); i++) {
207 | if (o.matches(BazaarNotifier.orders.get(hoveredText))) {
208 | drawOnSlot(chest.getSizeInventory(), j, 0xff00ff00);
209 | break;
210 | }
211 | }
212 | }
213 | }
214 | }
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/modules/module/SuggestionModule.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.modules.module;
2 |
3 | import cc.polyfrost.oneconfig.config.annotations.Slider;
4 | import cc.polyfrost.oneconfig.config.annotations.Switch;
5 | import cc.polyfrost.oneconfig.config.migration.JsonName;
6 | import cc.polyfrost.oneconfig.libs.universal.UMatrixStack;
7 | import dev.meyi.bn.BazaarNotifier;
8 | import dev.meyi.bn.modules.Module;
9 | import dev.meyi.bn.modules.ModuleName;
10 | import dev.meyi.bn.utilities.ColoredText;
11 | import dev.meyi.bn.utilities.RenderUtils;
12 | import dev.meyi.bn.utilities.Defaults;
13 | import java.awt.Color;
14 | import java.util.ArrayList;
15 | import java.util.LinkedList;
16 | import java.util.List;
17 | import net.minecraft.client.Minecraft;
18 | import org.lwjgl.opengl.GL11;
19 |
20 | public class SuggestionModule extends Module{
21 |
22 | public transient static final ModuleName type = ModuleName.SUGGESTION;
23 | public transient static List list = new LinkedList<>();
24 |
25 | @JsonName("suggestionListLength")
26 | @Slider(name = "Suggestion List Entries",
27 | category = "Suggestion Module",
28 | description = "The amount of entries in the Suggestion Module list",
29 | min = 1,max = 25,step = 1
30 | )
31 | public int suggestionListLength = Defaults.SUGGESTION_LIST_LENGTH;
32 |
33 | @Switch(name= "Use Profit per Hour", category = "Suggestion Module")
34 | public boolean useProfitPerHour = false;
35 |
36 | @JsonName("suggestionShowEnchantments")
37 | @Switch(name = "Show Enchantments",
38 | category = "Suggestion Module",
39 | description = "If the mod should recommend enchantments"
40 | )
41 | public boolean suggestionShowEnchantments = Defaults.SUGGESTION_SHOW_ENCHANTMENTS;
42 |
43 | public SuggestionModule() {
44 | super();
45 | }
46 |
47 | @Override
48 | protected void draw(UMatrixStack matrices, float x, float y, float scale, boolean example) {
49 | draw();
50 | }
51 |
52 | @Override
53 | protected float getWidth(float scale, boolean example) {
54 | return RenderUtils.getStringWidth(longestString)*scale + 2 * padding * scale;
55 | }
56 |
57 | @Override
58 | protected float getHeight(float scale, boolean example) {
59 | if(BazaarNotifier.config == null){
60 | return 100f*scale;
61 | }
62 | return (Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT
63 | * BazaarNotifier.config.suggestionModule.suggestionListLength
64 | + BazaarNotifier.config.suggestionModule.suggestionListLength * 2)*scale - 2
65 | + 2 * padding * scale;
66 | }
67 |
68 | @Override
69 | public void draw() {
70 | GL11.glTranslated(0, 0, 1);
71 | drawBounds();
72 | if (list.size() != 0) {
73 | ArrayList> items = new ArrayList<>();
74 | for (int i = shift; i < BazaarNotifier.config.suggestionModule.suggestionListLength + shift; i++) {
75 | ArrayList message = new ArrayList<>();
76 | message.add(new ColoredText((i + 1) + ". ", BazaarNotifier.config.numberColor.toJavaColor()));
77 | message.add(new ColoredText(list.get(i)[0], BazaarNotifier.config.itemColor.toJavaColor()));
78 | message.add(new ColoredText(" - ", BazaarNotifier.config.infoColor.toJavaColor()));
79 | message.add(new ColoredText("EP: ", Color.RED));
80 | message.add(new ColoredText("" + BazaarNotifier.df.format(Double.parseDouble(list.get(i)[1]) *
81 | (useProfitPerHour?60:1)), Color.ORANGE));
82 | items.add(message);
83 | }
84 | longestString = RenderUtils.getLongestString(items);
85 | RenderUtils.drawColorfulParagraph(items, (int)position.getX() + padding, (int)position.getY() + padding, scale);
86 | } else {
87 | RenderUtils.drawCenteredString("Waiting for bazaar data", (int)position.getX(), (int)position.getY(), 0xAAAAAA, scale);
88 | //Todo add height and width
89 | }
90 | GL11.glTranslated(0, 0, -1);
91 | }
92 |
93 | @Override
94 | protected void reset() {
95 | position.setPosition(Defaults.SUGGESTION_MODULE_X,Defaults.SUGGESTION_MODULE_Y);
96 | scale = 1;
97 | enabled = true;
98 | BazaarNotifier.config.suggestionModule.suggestionListLength = Defaults.SUGGESTION_LIST_LENGTH;
99 | }
100 |
101 | @Override
102 | public String name() {
103 | return ModuleName.SUGGESTION.name();
104 | }
105 |
106 | @Override
107 | protected boolean shouldDrawBounds() {
108 | return true;
109 | }
110 |
111 | @Override
112 | protected int getMaxShift() {
113 | return list.size() - BazaarNotifier.config.suggestionModule.suggestionListLength;
114 | }
115 |
116 | }
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/utilities/ColoredText.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.utilities;
2 |
3 | import java.awt.Color;
4 |
5 | public class ColoredText {
6 | public String string;
7 | public Color color;
8 |
9 | public ColoredText(String string, Color color){
10 | this.string = string;
11 | this.color = color;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/utilities/Defaults.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.utilities;
2 |
3 |
4 | public class Defaults {
5 |
6 | public static final int SUGGESTION_MODULE_X = 5;
7 | public static final int SUGGESTION_MODULE_Y = 10;
8 | public static final int BANK_MODULE_X = 5;
9 | public static final int BANK_MODULE_Y = 10;
10 | public static final int NOTIFICATION_MODULE_X = 5;
11 | public static final int NOTIFICATION_MODULE_Y = 10;
12 | public static final int CRAFTING_MODULE_X = 5;
13 | public static final int CRAFTING_MODULE_Y = 10;
14 | public static final int CRAFTING_LIST_LENGTH = 10;
15 | public static final int SUGGESTION_LIST_LENGTH = 10;
16 | public static final int CRAFTING_SORTING_OPTION = 0;
17 | public static final boolean INSTANT_SELL_PROFIT = true;
18 | public static final boolean SELL_OFFER_PROFIT = true;
19 | public static final boolean PROFIT_PER_MIL = true;
20 | public static final boolean COLLECTION_CHECKING = false;
21 | public static final boolean SEND_CHAT_MESSAGES = true;
22 | public static final boolean USE_BUY_ORDERS = true;
23 | public static final boolean SUGGESTION_SHOW_ENCHANTMENTS = true;
24 | public static final boolean BANK_RAW_DIFFERENCE = false;
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/utilities/ReflectionHelper.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.utilities;
2 |
3 | import java.lang.reflect.Field;
4 |
5 | import net.minecraft.client.gui.inventory.GuiChest;
6 | import net.minecraft.inventory.IInventory;
7 | import net.minecraft.launchwrapper.Launch;
8 |
9 | public class ReflectionHelper {
10 | private static Field lowerChestInventory;
11 | public static void setup() {
12 | try {
13 | lowerChestInventory = GuiChest.class.getDeclaredField((Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment") ? "lowerChestInventory" : "field_147015_w");
14 | lowerChestInventory.setAccessible(true);
15 | } catch (NoSuchFieldException e) {
16 | try{
17 | lowerChestInventory = GuiChest.class.getDeclaredField("lowerChestInventory");
18 | lowerChestInventory.setAccessible(true);
19 | }catch (NoSuchFieldException ignored){
20 | lowerChestInventory = null;
21 | e.printStackTrace();
22 | }
23 | }
24 | }
25 |
26 | public static IInventory getLowerChestInventory(GuiChest g) {
27 | try {
28 | if (lowerChestInventory != null) {
29 | return (IInventory)lowerChestInventory.get(g);
30 | }
31 | } catch (IllegalAccessException ignored) {
32 | }
33 | return null;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/utilities/RenderUtils.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.utilities;
2 |
3 | import dev.meyi.bn.BazaarNotifier;
4 | import dev.meyi.bn.json.Order;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import net.minecraft.client.Minecraft;
10 | import net.minecraft.client.gui.FontRenderer;
11 | import net.minecraft.util.ChatComponentText;
12 | import net.minecraft.util.EnumChatFormatting;
13 | import org.lwjgl.opengl.GL11;
14 |
15 |
16 | public class RenderUtils {
17 |
18 |
19 | /**
20 | * @param renderer Minecraft's renderer
21 | * @param x horizontal coordinate for rendering
22 | * @param y vertical coordinate for rendering
23 | * @param message text split up by color in order of rendering
24 | * @param dropShadow should the shadow have text
25 | * @return length of the entire text
26 | */
27 | public static int drawMulticoloredString(FontRenderer renderer, int x, int y,
28 | List message, boolean dropShadow, float moduleScale) {
29 |
30 | int renderLength = 0;
31 | for (ColoredText substring : message) {
32 | GL11.glScalef(moduleScale, moduleScale, 1);
33 | renderer.drawString(substring.string, (x + renderLength) / moduleScale, y / moduleScale,
34 | substring.color.getRGB(),
35 | dropShadow);
36 | renderLength += renderer.getStringWidth(substring.string) * moduleScale;
37 | GL11.glScalef((float) Math.pow(moduleScale, -1), (float) Math.pow(moduleScale, -1), 1);
38 | }
39 |
40 | return renderLength;
41 | }
42 |
43 | public static int drawColorfulParagraph(ArrayList> items, int x, int y,
44 | float moduleScale) {
45 | float longestXString = 0;
46 | for (int i = 0; i < items.size(); i++) {
47 | float fontHeight =
48 | (Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT + 2) * i * moduleScale;
49 | int length = RenderUtils
50 | .drawMulticoloredString(Minecraft.getMinecraft().fontRendererObj,
51 | x, y
52 | + (int) fontHeight,
53 | items.get(i), false, moduleScale);
54 | if (length > longestXString) {
55 | longestXString = length;
56 | }
57 | }
58 | return (int) longestXString;
59 | }
60 | public static String getLongestString(ArrayList> items) {
61 | FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
62 | float longestXString = 0;
63 | String longestString = "";
64 | for (List item : items) {
65 | StringBuilder builder = new StringBuilder("");
66 | for (ColoredText entry : item) {
67 | builder.append(entry.string);
68 | }
69 |
70 | int length = fontRenderer.getStringWidth(builder.toString());
71 | if (length > longestXString) {
72 | longestXString = length;
73 | longestString = builder.toString();
74 | }
75 | }
76 | return longestString;
77 | }
78 | public static int getStringWidth(String string) {
79 | FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
80 | return fontRenderer.getStringWidth(string);
81 | }
82 |
83 | public static void chatNotification(Order order, String notification) {
84 | if (!BazaarNotifier.config.showChatMessages) {
85 | return;
86 | }
87 | EnumChatFormatting messageColor =
88 | (notification.equalsIgnoreCase("REVIVED") ? EnumChatFormatting.GREEN
89 | : order.type.equals(Order.OrderType.BUY) ? EnumChatFormatting.DARK_PURPLE
90 | : EnumChatFormatting.BLUE);
91 | if (Minecraft.getMinecraft().thePlayer != null) {
92 | Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
93 | messageColor + order.type.longName
94 | + EnumChatFormatting.GRAY + " for "
95 | + messageColor + BazaarNotifier.dfNoDecimal
96 | .format(order.startAmount)
97 | + EnumChatFormatting.GRAY + "x " + messageColor
98 | + order.product
99 | + EnumChatFormatting.YELLOW
100 | + " " + notification + " " + EnumChatFormatting.GRAY + "("
101 | + messageColor + BazaarNotifier.df.format(order.pricePerUnit)
102 | + EnumChatFormatting.GRAY + ")"
103 | ));
104 | }
105 | }
106 |
107 | public static void drawCenteredString(String text, int x, int y, int color, float moduleScale) {
108 | x = (int) (x / moduleScale + 200 / 4);
109 | y = (int) (y / moduleScale + (Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT * 6));
110 | GL11.glScalef(moduleScale, moduleScale, 1);
111 | Minecraft.getMinecraft().fontRendererObj.drawString(text, x, y, color);
112 | GL11.glScalef((float) Math.pow(moduleScale, -1), (float) Math.pow(moduleScale, -1), 1);
113 | }
114 | }
115 |
116 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/utilities/ScheduledEvents.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.utilities;
2 |
3 | import dev.meyi.bn.BazaarNotifier;
4 | import dev.meyi.bn.json.Order;
5 | import dev.meyi.bn.modules.calc.CraftingCalculator;
6 | import dev.meyi.bn.modules.calc.SuggestionCalculator;
7 | import java.text.DateFormat;
8 | import java.util.Date;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 | import java.util.Map.Entry;
12 | import java.util.concurrent.Executors;
13 | import java.util.concurrent.ScheduledExecutorService;
14 | import java.util.concurrent.TimeUnit;
15 | import net.minecraft.client.Minecraft;
16 | import net.minecraft.util.ChatComponentText;
17 | import net.minecraft.util.EnumChatFormatting;
18 |
19 | public class ScheduledEvents {
20 |
21 | public static ScheduledEvents instance;
22 |
23 | private final Map executors = new HashMap<>();
24 |
25 | private ScheduledEvents() {
26 | executors.put("bazaar", getScheduler("bazaar"));
27 | executors.put("notification", getScheduler("notification"));
28 | executors.put("crafting", getScheduler("crafting"));
29 | executors.put("suggestion", getScheduler("suggestion"));
30 | executors.put("collection", getScheduler("collection"));
31 | executors.put("purse", getScheduler("purse"));
32 |
33 | shutdownWatcher();
34 | }
35 |
36 | public static void create() {
37 | CraftingCalculator.getUnlockedRecipes();
38 | if (instance == null) {
39 | instance = new ScheduledEvents();
40 | }
41 | }
42 |
43 | private ScheduledExecutorService getScheduler(String key) {
44 | switch (key) {
45 | case "bazaar":
46 | return getBazaarData();
47 | case "notification":
48 | return notificationLoop();
49 | case "crafting":
50 | return craftingBankLoop();
51 | case "suggestion":
52 | return suggestionLoop();
53 | case "collection":
54 | return collectionLoop();
55 | }
56 | return null;
57 | }
58 |
59 | private void shutdownWatcher() {
60 | Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
61 | for (Entry entry : executors.entrySet()) {
62 | if (entry.getValue().isShutdown()) {
63 | if (Minecraft.getMinecraft() != null && Minecraft.getMinecraft().thePlayer != null) {
64 | Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
65 | BazaarNotifier.prefix + EnumChatFormatting.RED + "Something has caused the " + entry
66 | .getKey()
67 | + " scheduler to crash. Please report this to the discord server (/bn discord)"));
68 |
69 | }
70 | executors.put(entry.getKey(), getScheduler(entry.getKey()));
71 | }
72 | }
73 | }, 1, 1, TimeUnit.MINUTES);
74 | }
75 |
76 | public ScheduledExecutorService craftingBankLoop() {
77 | ScheduledExecutorService ex = Executors.newScheduledThreadPool(1);
78 | ex.scheduleAtFixedRate(() -> {
79 | if (BazaarNotifier.activeBazaar) {
80 | try {
81 | CraftingCalculator.getBestEnchantRecipes();
82 |
83 | // Reset the bank calculator on the new day
84 | // It probably isn't necessary to do this in a scheduler, but here we are.
85 | long currentTime = System.currentTimeMillis();
86 | Date reset = new Date(currentTime - (currentTime % 86400000));
87 | if (reset.after(new Date(BazaarNotifier.config.lastLogin))) {
88 | BazaarNotifier.config.lastLogin = System.currentTimeMillis();
89 | BazaarNotifier.config.bankModule.bazaarDailyAmount = 1E10;
90 | }
91 | } catch (Exception t) {
92 | t.printStackTrace();
93 | }
94 | }
95 | }, 5, 5, TimeUnit.SECONDS);
96 | return ex;
97 | }
98 |
99 | public ScheduledExecutorService getBazaarData() {
100 | ScheduledExecutorService ex = Executors.newScheduledThreadPool(1);
101 | ex.scheduleAtFixedRate(() -> {
102 | if (BazaarNotifier.activeBazaar) {
103 | try {
104 | BazaarNotifier.bazaarDataRaw = Utils.getBazaarData();
105 | } catch (Exception t) {
106 | t.printStackTrace();
107 | }
108 | }
109 | }, 0, 2, TimeUnit.SECONDS);
110 | return ex;
111 | }
112 |
113 | public ScheduledExecutorService suggestionLoop() {
114 | ScheduledExecutorService ex = Executors.newScheduledThreadPool(1);
115 | ex.scheduleAtFixedRate(() -> {
116 | if (BazaarNotifier.activeBazaar) {
117 | try {
118 | SuggestionCalculator.basic();
119 | } catch (Exception t) {
120 | t.printStackTrace();
121 | }
122 | }
123 | }, 5, 5, TimeUnit.SECONDS);
124 | return ex;
125 | }
126 |
127 | public ScheduledExecutorService collectionLoop() {
128 | ScheduledExecutorService ex = Executors.newScheduledThreadPool(1);
129 | ex.scheduleAtFixedRate(() -> {
130 | if (BazaarNotifier.activeBazaar) {
131 | try {
132 | CraftingCalculator.getUnlockedRecipes();
133 | } catch (Exception t) {
134 | t.printStackTrace();
135 | }
136 | }
137 | }, 5, 5, TimeUnit.MINUTES);
138 | return ex;
139 | }
140 |
141 |
142 | public ScheduledExecutorService notificationLoop() {
143 | ScheduledExecutorService ex = Executors.newScheduledThreadPool(1);
144 | ex.scheduleAtFixedRate(() -> {
145 | if (BazaarNotifier.activeBazaar) {
146 | try {
147 | for (Order order : BazaarNotifier.orders) {
148 | order.updateStatus();
149 | }
150 | } catch (Exception t) {
151 | t.printStackTrace();
152 | }
153 | }
154 | }, 0, 2, TimeUnit.SECONDS);
155 |
156 | return ex;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/java/dev/meyi/bn/utilities/Utils.java:
--------------------------------------------------------------------------------
1 | package dev.meyi.bn.utilities;
2 |
3 | import com.google.common.collect.BiMap;
4 | import com.google.common.collect.HashBiMap;
5 | import com.google.gson.Gson;
6 | import com.google.gson.JsonElement;
7 | import com.google.gson.JsonObject;
8 | import com.google.gson.JsonSyntaxException;
9 | import com.google.gson.stream.JsonReader;
10 | import dev.meyi.bn.BazaarNotifier;
11 | import dev.meyi.bn.json.resp.BazaarResponse;
12 | import java.io.BufferedReader;
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.io.InputStreamReader;
16 | import java.nio.charset.StandardCharsets;
17 | import java.nio.file.Files;
18 | import java.nio.file.Paths;
19 | import java.security.KeyManagementException;
20 | import java.security.NoSuchAlgorithmException;
21 | import java.security.cert.CertificateException;
22 | import java.security.cert.X509Certificate;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 | import java.util.Map;
27 | import java.util.Set;
28 | import net.minecraft.client.Minecraft;
29 | import net.minecraft.item.ItemStack;
30 | import net.minecraft.nbt.NBTTagList;
31 | import org.apache.commons.io.IOUtils;
32 | import org.apache.commons.lang3.StringUtils;
33 | import org.apache.http.HttpResponse;
34 | import org.apache.http.client.methods.HttpGet;
35 | import org.apache.http.impl.client.CloseableHttpClient;
36 | import org.apache.http.impl.client.HttpClientBuilder;
37 |
38 | import javax.net.ssl.SSLContext;
39 | import javax.net.ssl.TrustManager;
40 | import javax.net.ssl.X509TrustManager;
41 |
42 | public class Utils {
43 | private static final TrustManager[] trustAllCerts = new TrustManager[] {
44 | new X509TrustManager() {
45 | @Override
46 | public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {
47 | }
48 |
49 | @Override
50 | public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {
51 | }
52 |
53 | @Override
54 | public X509Certificate[] getAcceptedIssuers() {
55 | return null;
56 | }
57 |
58 | }
59 | };
60 | private static String playerUUID = "";
61 |
62 | public static BazaarResponse getBazaarData() throws IOException {
63 | Gson gson = new Gson();
64 | CloseableHttpClient client = HttpClientBuilder.create().build();
65 | HttpGet request = new HttpGet(
66 | "https://api.hypixel.net/v2/skyblock/bazaar");
67 | HttpResponse response = client.execute(request);
68 |
69 | String result = IOUtils.toString(new BufferedReader
70 | (new InputStreamReader(
71 | response.getEntity().getContent())));
72 |
73 | client.close();
74 |
75 | if (isJSONValid(result)) {
76 | return gson.fromJson(result, BazaarResponse.class);
77 | } else {
78 | return new BazaarResponse(false, 0, null);
79 | }
80 | }
81 |
82 |
83 | /**
84 | * Waiting until a proper backend is built out before completing this feature.
85 | *
86 | * @see the
88 | * old code
89 | */
90 | public static List unlockedRecipes() throws IOException {
91 | Gson gson = new Gson();
92 | if (BazaarNotifier.config.collectionCheck) {
93 | try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
94 | if (playerUUID.equals("")) {
95 | HttpGet request = new HttpGet(
96 | "https://api.mojang.com/users/profiles/minecraft/" + Minecraft.getMinecraft()
97 | .getSession().getUsername()); // Change this to your username if testing
98 | HttpResponse response = client.execute(request);
99 |
100 | String uuidResponse = IOUtils
101 | .toString(
102 | new BufferedReader(new InputStreamReader(response.getEntity().getContent())));
103 |
104 | try {
105 | playerUUID = gson.fromJson(uuidResponse, JsonObject.class).get("id").getAsString();
106 | } catch (JsonSyntaxException e) {
107 | return null;
108 | }
109 | }
110 | }
111 | }
112 | return null;
113 | }
114 |
115 | public static boolean isJSONValid(String jsonInString) {
116 | Gson gson = new Gson();
117 | try {
118 | gson.fromJson(jsonInString, JsonObject.class);
119 | return true;
120 | } catch (Exception ex) {
121 | return false;
122 | }
123 | }
124 |
125 | public static void updateResources() throws IOException, KeyManagementException, NoSuchAlgorithmException, ClassCastException {
126 | Gson gson = new Gson();
127 | HttpGet request;
128 | HttpResponse response;
129 | SSLContext sc = SSLContext.getInstance("SSL");
130 | sc.init(null, trustAllCerts, new java.security.SecureRandom());
131 | CloseableHttpClient client = HttpClientBuilder.create().setSslcontext(sc).build();
132 | request = new HttpGet(BazaarNotifier.RESOURCE_LOCATION);
133 | response = client.execute(request);
134 |
135 | JsonReader jsonReader = new JsonReader(
136 | new BufferedReader(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8)));
137 | jsonReader.setLenient(true);
138 | try {
139 | BazaarNotifier.resources = gson.fromJson(jsonReader, JsonObject.class);
140 | BazaarNotifier.bazaarConv = jsonToBimap(
141 | BazaarNotifier.resources.getAsJsonObject("bazaarConversions"));
142 | BazaarNotifier.enchantCraftingList = BazaarNotifier.resources
143 | .getAsJsonObject("enchantCraftingList");
144 | } catch (JsonSyntaxException | ClassCastException e) { //ClassCastException is thrown when GitHub is down
145 | e.printStackTrace();
146 | } finally {
147 | client.close();
148 | }
149 | }
150 |
151 | public static BiMap jsonToBimap(JsonObject jsonObject) {
152 | BiMap b = HashBiMap.create();
153 | Set> entries = jsonObject.entrySet();
154 | for (Map.Entry entry : entries) {
155 | try {
156 | b.put(entry.getKey(), jsonObject.get(entry.getKey()).getAsString());
157 | } catch (IllegalArgumentException ignored) {
158 | }
159 | }
160 | return b;
161 | }
162 |
163 | public static void saveResources(File file, JsonObject resources) {
164 | Gson gson = new Gson();
165 | try {
166 | if (!file.isFile()) {
167 | //noinspection ResultOfMethodCallIgnored
168 | file.createNewFile();
169 | }
170 | Files.write(Paths.get(file.getAbsolutePath()),
171 | gson.toJson(resources).getBytes(StandardCharsets.UTF_8));
172 | } catch (IOException e) {
173 | e.printStackTrace();
174 | }
175 | }
176 |
177 | public static String[] getItemIdFromName(String userInput) {
178 | int threshold = userInput.length() / 3;
179 | String closestConversion = "";
180 | int minLevenshteinDistance = threshold + 1;
181 |
182 | for (String key : BazaarNotifier.bazaarConv.values()) {
183 | int levenshteinDistance = StringUtils
184 | .getLevenshteinDistance(userInput.toLowerCase(), key.toLowerCase(), threshold);
185 | if (levenshteinDistance != -1) {
186 | if (minLevenshteinDistance > levenshteinDistance) {
187 | minLevenshteinDistance = levenshteinDistance;
188 | closestConversion = key;
189 | if (levenshteinDistance == 0) {
190 | break;
191 | }
192 | }
193 | }
194 | }
195 |
196 | return new String[]{closestConversion,
197 | BazaarNotifier.bazaarConv.inverse().getOrDefault(closestConversion, "")};
198 | }
199 |
200 | public static List getLoreFromItemStack(ItemStack item) {
201 | NBTTagList lorePreFilter = item.getTagCompound().getCompoundTag("display")
202 | .getTagList("Lore", 8);
203 |
204 | List lore = new ArrayList<>();
205 |
206 | for (int j = 0; j < lorePreFilter.tagCount(); j++) {
207 | lore.add(net.minecraft.util.StringUtils.stripControlCodes(lorePreFilter.getStringTagAt(j)));
208 | }
209 |
210 | return lore;
211 | }
212 |
213 | public static int getOrderAmountLeft(List lore, int totalAmount) {
214 | int amountLeft;
215 | if (lore.get(3).startsWith("Filled:")) {
216 | if (lore.get(3).split(" ")[2].contains("100%")) {
217 | amountLeft = 0;
218 | } else {
219 | String intToParse = lore.get(3).split(" ")[1].split("/")[0];
220 | int amountFulfilled;
221 |
222 | if (intToParse.contains("k")) {
223 | amountFulfilled = (int) (Double.parseDouble(intToParse.replace("k", "")) * 1000);
224 | } else {
225 | amountFulfilled = Integer.parseInt(intToParse);
226 | }
227 |
228 | amountLeft = totalAmount - amountFulfilled;
229 | }
230 | } else {
231 | amountLeft = totalAmount;
232 | }
233 | return amountLeft;
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/main/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/symt/BazaarNotifier/b4b089d210dac724b50f494cb781427f2366052b/src/main/resources/icon.png
--------------------------------------------------------------------------------
/src/main/resources/mcmod.info:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "modid": "BazaarNotifier",
4 | "name": "BazaarNotifier",
5 | "description": "Notifies when your bazaar orders are outdated.",
6 | "version": "${version}",
7 | "mcversion": "${mcversion}",
8 | "url": "https://github.com/symt/BazaarNotifier",
9 | "updateUrl": "https://github.com/symt/BazaarNotifier/releases",
10 | "authorList": ["meyi"],
11 | "credits": "meyi",
12 | "logoFile": "",
13 | "screenshots": [],
14 | "dependencies": []
15 | }
16 | ]
--------------------------------------------------------------------------------
/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/symt/BazaarNotifier/b4b089d210dac724b50f494cb781427f2366052b/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------