commit d7d42be4d9df7811c62a953928c1222a732b2433 Author: marduk Date: Sun Aug 28 18:50:53 2022 +0300 init commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f811f6a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Disable autocrlf on generated files, they always generate with LF +# Add any extra files or paths here to make git stop saying they +# are changed when only line endings change. +src/generated/**/.cache/cache text eol=lf +src/generated/**/*.json text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12f8644 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# eclipse +bin +*.launch +.settings +.metadata +.classpath +.project + +# idea +out +*.ipr +*.iws +*.iml +.idea + +# gradle +build +.gradle + +# other +eclipse +run + +# Files from Forge MDK +forge*changelog.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b569232 --- /dev/null +++ b/LICENSE @@ -0,0 +1,353 @@ +Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright and +certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + +Considerations for licensors: Our public licenses are intended for use +by those authorized to give the public permission to use material in +ways otherwise restricted by copyright and certain other rights. Our +licenses are irrevocable. Licensors should read and understand the terms +and conditions of the license they choose before applying it. Licensors +should also secure all rights necessary before applying our licenses so +that the public can reuse the material as expected. Licensors should +clearly mark any material not subject to the license. This includes +other CC-licensed material, or material used under an exception or +limitation to copyright. More considerations for licensors : +wiki.creativecommons.org/Considerations_for_licensors + +Considerations for the public: By using one of our public licenses, a +licensor grants the public permission to use the licensed material under +specified terms and conditions. If the licensor's permission is not +necessary for any reason–for example, because of any applicable +exception or limitation to copyright–then that use is not regulated by +the license. Our licenses grant only permissions under copyright and +certain other rights that a licensor has authority to grant. Use of the +licensed material may still be restricted for other reasons, including +because others have copyright or other rights in the material. A +licensor may make special requests, such as asking that all changes be +marked or described. Although not required by our licenses, you are +encouraged to respect those requests where reasonable. More +considerations for the public : +wiki.creativecommons.org/Considerations_for_licensees + +Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International +Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International Public License +("Public License"). To the extent this Public License may be interpreted +as a contract, You are granted the Licensed Rights in consideration of +Your acceptance of these terms and conditions, and the Licensor grants +You such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. + +- a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material and + in which the Licensed Material is translated, altered, arranged, + transformed, or otherwise modified in a manner requiring permission + under the Copyright and Similar Rights held by the Licensor. For + purposes of this Public License, where the Licensed Material is a + musical work, performance, or sound recording, Adapted Material is + always produced where the Licensed Material is synched in timed + relation with a moving image. +- b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. +- c. BY-NC-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative Commons + as essentially the equivalent of this Public License. +- d. Copyright and Similar Rights means copyright and/or similar + rights closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or categorized. + For purposes of this Public License, the rights specified in Section + 2(b)(1)-(2) are not Copyright and Similar Rights. +- e. Effective Technological Measures means those measures that, in + the absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright Treaty + adopted on December 20, 1996, and/or similar international + agreements. +- f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. +- g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution, NonCommercial, and ShareAlike. +- h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public License. +- i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. +- j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. +- k. NonCommercial means not primarily intended for or directed + towards commercial advantage or monetary compensation. For purposes + of this Public License, the exchange of the Licensed Material for + other material subject to Copyright and Similar Rights by digital + file-sharing or similar means is NonCommercial provided there is no + payment of monetary compensation in connection with the exchange. +- l. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such as + reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the public + may access the material from a place and at a time individually + chosen by them. +- m. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially equivalent + rights anywhere in the world. +- n. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + +- a. License grant. + - 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + - A. reproduce and Share the Licensed Material, in whole or in + part, for NonCommercial purposes only; and + - B. produce, reproduce, and Share Adapted Material for + NonCommercial purposes only. + - 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with its + terms and conditions. + - 3. Term. The term of this Public License is specified in Section + 6(a). + - 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in all + media and formats whether now known or hereafter created, and to + make technical modifications necessary to do so. The Licensor + waives and/or agrees not to assert any right or authority to + forbid You from making technical modifications necessary to + exercise the Licensed Rights, including technical modifications + necessary to circumvent Effective Technological Measures. For + purposes of this Public License, simply making modifications + authorized by this Section 2(a)(4) never produces Adapted + Material. + - 5. Downstream recipients. + - A. Offer from the Licensor – Licensed Material. Every + recipient of the Licensed Material automatically receives an + offer from the Licensor to exercise the Licensed Rights + under the terms and conditions of this Public License. + - B. Additional offer from the Licensor – Adapted Material. + Every recipient of Adapted Material from You automatically + receives an offer from the Licensor to exercise the Licensed + Rights in the Adapted Material under the conditions of the + Adapter's License You apply. + - C. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or apply + any Effective Technological Measures to, the Licensed + Material if doing so restricts exercise of the Licensed + Rights by any recipient of the Licensed Material. + - 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You are, + or that Your use of the Licensed Material is, connected with, or + sponsored, endorsed, or granted official status by, the Licensor + or others designated to receive attribution as provided in + Section 3(a)(1)(A)(i). +- b. Other rights. + - 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, privacy, + and/or other similar personality rights; however, to the extent + possible, the Licensor waives and/or agrees not to assert any + such rights held by the Licensor to the limited extent necessary + to allow You to exercise the Licensed Rights, but not otherwise. + - 2. Patent and trademark rights are not licensed under this + Public License. + - 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society under + any voluntary or waivable statutory or compulsory licensing + scheme. In all other cases the Licensor expressly reserves any + right to collect such royalties, including when the Licensed + Material is used other than for NonCommercial purposes. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + +- a. Attribution. + - 1. If You Share the Licensed Material (including in modified + form), You must: + - A. retain the following if it is supplied by the Licensor + with the Licensed Material: + - i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by the + Licensor (including by pseudonym if designated); + - ii. a copyright notice; + - iii. a notice that refers to this Public License; + - iv. a notice that refers to the disclaimer of + warranties; + - v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + - B. indicate if You modified the Licensed Material and retain + an indication of any previous modifications; and + - C. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + - 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required information. + - 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. +- b. ShareAlike.In addition to the conditions in Section 3(a), if You + Share Adapted Material You produce, the following conditions also + apply. + - 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or later, + or a BY-NC-SA Compatible License. + - 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition in + any reasonable manner based on the medium, means, and context in + which You Share Adapted Material. + - 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological Measures + to, Adapted Material that restrict exercise of the rights + granted under the Adapter's License You apply. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply +to Your use of the Licensed Material: + +- a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial portion + of the contents of the database for NonCommercial purposes only; +- b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and +- c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + For the avoidance of doubt, this Section 4 supplements and does not + replace Your obligations under this Public License where the + Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + +- a. Unless otherwise separately undertaken by the Licensor, to the + extent possible, the Licensor offers the Licensed Material as-is and + as-available, and makes no representations or warranties of any kind + concerning the Licensed Material, whether express, implied, + statutory, or other. This includes, without limitation, warranties + of title, merchantability, fitness for a particular purpose, + non-infringement, absence of latent or other defects, accuracy, or + the presence or absence of errors, whether or not known or + discoverable. Where disclaimers of warranties are not allowed in + full or in part, this disclaimer may not apply to You. +- b. To the extent possible, in no event will the Licensor be liable + to You on any legal theory (including, without limitation, + negligence) or otherwise for any direct, special, indirect, + incidental, consequential, punitive, exemplary, or other losses, + costs, expenses, or damages arising out of this Public License or + use of the Licensed Material, even if the Licensor has been advised + of the possibility of such losses, costs, expenses, or damages. + Where a limitation of liability is not allowed in full or in part, + this limitation may not apply to You. +- c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent possible, + most closely approximates an absolute disclaimer and waiver of all + liability. + +Section 6 – Term and Termination. + +- a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. +- b. Where Your right to use the Licensed Material has terminated + under Section 6(a), it reinstates: + + - 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the violation; + or + - 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations of + this Public License. + +- c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. +- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + +Section 7 – Other Terms and Conditions. + +- a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. +- b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + +Section 8 – Interpretation. + +- a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. +- b. To the extent possible, if any provision of this Public License + is deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. +- c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. +- d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, +Creative Commons may elect to apply one of its public licenses to +material it publishes and in those instances will be considered the +"Licensor." The text of the Creative Commons public licenses is +dedicated to the public domain under the CC0 Public Domain Dedication. +Except for the limited purpose of indicating that material is shared +under a Creative Commons public license or as otherwise permitted by the +Creative Commons policies published at creativecommons.org/policies, +Creative Commons does not authorize the use of the trademark "Creative +Commons" or any other trademark or logo of Creative Commons without its +prior written consent including, without limitation, in connection with +any unauthorized modifications to any of its public licenses or any +other arrangements, understandings, or agreements concerning use of +licensed material. For the avoidance of doubt, this paragraph does not +form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ae61c2 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# Timelord + +Ваш лучший помощник в вопросах настройки времени! На Forge версии 1.16.5 и только. Отдельная благодарность Мсе за большую часть логики, которую я взял у него. + + +## Как это работает + +При запуске игры у вас создадутся файлики в ./config/timelord - на каждый дименшен по файлу. В этих файлах, собственно, все и настраивается. Для каждого дименшена файл выглядит как-то так: + +```json +{ + "active": false, + "rain": { + "endlessRain": false, + "endlessThunder": false, + "income": 0, + "outcome": 0, + "thunder": 0, + "min": 0, + "max": 0, + "dice": 0 + }, + "time": { + "hours_offset": 0, + "scale": 0.0, + "fixedDayTime": -1 + } +} +``` + +Для того, чтобы таймлорд работал, вам нужно изменить active на true. По умолчанию он не работает ни для какого дименшена. + +Начнем c time. + +1. Если задан fixedDayTime в пределах от 0 до 24000, то назначается он. Это позволяет сделать в дименшене фиксированное время. Значения остальных полей игнорируются, т.е. если вы не хотите, чтобы было фиксированное время, то сделайте -1 или 24001 и так далее. +2. Если scale больше нуля, то считается, что вы хотите именно его. Скейл это ускорение или замедление течения времени относительно обычного времени. Например, если scale 3.0, то время будет идти в три раза быстрее, а если 0.5 - то в 2 раза медленнее. Если активируется scale, то игнорируется hours_offset. +3. Наконец, hours_offset. Он может быть от -18 до 18 и выражает собой отступ в часах от текущего серверного времени. Он может быть 0, тогда игровое время будет полностью соответствовать времени сервера. Если вы хотите и его отключить, то сделайте его больше 18 или меньше -18. +Таким образом, если у вас hours_offset: 9, scale: 0 и fixed_day_time: -1, то время будет идти как в ванилле. Это может быть полезно, если вам нужно сохранить фичи дождя. + +Перейдем к rain. + +Дождь работает по следующему принципу - есть условная "ванная", которая у каждого мира своя. В эту замечательную ванную, когда нет дождя, каждый тик добавляется incomе. Если накопленный объем больше min, то каждый тик делается бросок дайса с dice граней. Если выпадает 0, то дайс считается успешным и начинается дождь. То есть, например, если вы хотите, чтобы шанс дождя был 50% (напомню, что чек делается каждый тик, поэтому это скорее всего означает мгновенный дождь), то можете задать dice = 1. Если объем накопленного дождя дошел до maх, а дождь так и не начался, то он случается вне зависимости от результатов ролла. При этом, если в момент начала дождя текущий накопленный дождь был выше thunder, то начнется гроза. Когда дождь начался, то каждый тик он забирает outcome из накопленного запаса "ванны", и когда этот запас доходит до 0, отключает дождь. Может звучать сложно, но вот вам пример: +```json +{ + "rain": { + "endlessRain": false, + "endlessThunder": false, + "income": 1, + "outcome": 5, + "thunder": 7500, + "min": 5000, + "max": 10000, + "dice": 5000 + } +} +``` +В данной ситуации, каждый тик добавляется по 1 дождю в "ванную", пока оно не дойдет до 5 тысяч. То есть через 5000 тиков каждый тик будет кидаться дайс, и если выпадает 0, то случается дождь. Если при этом дождь должен начаться после того, как его накопилось 7500, то будет гроза. Если дайсик ни разу не прокнул и накопленный дождь дошел до 10000, то он начнется, и это обязательно будет гроза (поскольку 10000 > 7500). + +Так же есть endlessRain и endlessThunder. Они отключают всю остальную логику, просто включая дождь или грозу, если они должны быть. Учтите, что технически гроза может быть без дождя, но я не уверен, как именно это работает. Так же учтите, что если вы включили endlessRain, то включение endlessThunder ничего не сделает - их нужно включать одновременно. Поэтому если вам нужны обе эти штуки, то включайте их одновременно. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..21fb61c --- /dev/null +++ b/build.gradle @@ -0,0 +1,176 @@ +buildscript { + repositories { + maven { url = 'https://maven.minecraftforge.net' } + mavenCentral() + } + dependencies { + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true + } +} +apply plugin: 'net.minecraftforge.gradle' +// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. +apply plugin: 'eclipse' +apply plugin: 'maven-publish' + +version = '1.0' +group = 'stargazer.timelord' // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = 'timelord' + +java.toolchain.languageVersion = JavaLanguageVersion.of(8) // Mojang ships Java 8 to end users, so your mod should target Java 8. + +println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) +minecraft { + // The mappings can be changed at any time, and must be in the following format. + // Channel: Version: + // snapshot YYYYMMDD Snapshot are built nightly. + // stable # Stables are built at the discretion of the MCP team. + // official MCVersion Official field/method names from Mojang mapping files + // + // You must be aware of the Mojang license when using the 'official' mappings. + // See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md + // + // Use non-default mappings at your own risk. they may not always work. + // Simply re-run your setup task after changing the mappings to update your workspace. + mappings channel: 'snapshot', version: '20210309-1.16.5' + // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. + + // accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') + + // Default run configurations. + // These can be tweaked, removed, or duplicated as needed. + runs { + client { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment + // The markers can be changed as needed. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + + mods { + examplemod { + source sourceSets.main + } + } + } + + server { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment + // The markers can be changed as needed. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + + mods { + examplemod { + source sourceSets.main + } + } + } + + data { + workingDirectory project.file('run') + + // Recommended logging data for a userdev environment + // The markers can be changed as needed. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + property 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + property 'forge.logging.console.level', 'debug' + + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + args '--mod', 'examplemod', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + + mods { + examplemod { + source sourceSets.main + } + } + } + } +} + +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } + +dependencies { + implementation 'org.jetbrains:annotations:20.1.0' + + // Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed + // that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied. + // The userdev artifact is a special name and will get all sorts of transformations applied to it. + minecraft 'net.minecraftforge:forge:1.16.5-36.2.34' + + // You may put jars on which you depend on in ./libs or you may define them like so.. + // compile "some.group:artifact:version:classifier" + // compile "some.group:artifact:version" + + // Real examples + // compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env + // compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env + + // The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime. + // provided 'com.mod-buildcraft:buildcraft:6.0.8:dev' + + // These dependencies get remapped to your current MCP mappings + // deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev' + + // For more info... + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html + +} + +// Example for how to get properties into the manifest for reading by the runtime.. +jar { + manifest { + attributes([ + "Specification-Title": "timelord", + "Specification-Vendor": "stargazer", + "Specification-Version": "1", // We are version 1 of ourselves + "Implementation-Title": project.name, + "Implementation-Version": "${version}", + "Implementation-Vendor" :"stargazer", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + ]) + } +} + +// Example configuration to allow publishing using the maven-publish task +// This is the preferred method to reobfuscate your jar file +jar.finalizedBy('reobfJar') +// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing +//publish.dependsOn('reobfJar') + +publishing { + publications { + mavenJava(MavenPublication) { + artifact jar + } + } + repositories { + maven { + url "file:///${project.projectDir}/mcmodsrepo" + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..878bf1f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +# This is required to provide enough memory for the Minecraft decompilation process. +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2e6e589 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/stargazer/timelord/Timelord.java b/src/main/java/stargazer/timelord/Timelord.java new file mode 100644 index 0000000..df97c82 --- /dev/null +++ b/src/main/java/stargazer/timelord/Timelord.java @@ -0,0 +1,109 @@ +package stargazer.timelord; + + +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.network.NetworkRegistry; +import net.minecraftforge.fml.network.PacketDistributor; +import net.minecraftforge.fml.network.simple.SimpleChannel; +import net.minecraftforge.server.command.ConfigCommand; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import stargazer.timelord.commands.TimelordCommands; +import stargazer.timelord.config.Configuration; +import stargazer.timelord.config.DimensionTimeConfig; +import stargazer.timelord.logic.TimelordClient; +import stargazer.timelord.logic.TimelordServer; +import stargazer.timelord.net.TimePacket; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static net.minecraftforge.fml.network.NetworkDirection.PLAY_TO_CLIENT; +import static stargazer.timelord.config.Configuration.initFromString; + +@Mod(Timelord.MODID) +public class Timelord { + public static final String MODID = "timelord"; + public static final Logger LOGGER = LogManager.getLogger(); + + public static final String PROTOCOL_VERSION = "1"; + public static SimpleChannel TIME_CHANNEL; + private static final Map dimensionsConfigMap = new HashMap<>(); + + public static Map getDimensionsConfigMap() { + return dimensionsConfigMap; + } + + public static void putStringIntoDimensionsConfigMap(ResourceLocation resourceLocation, String string) { + dimensionsConfigMap.put(resourceLocation, initFromString(string)); + } + + + public Timelord() { + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onCommonSetupEvent); + MinecraftForge.EVENT_BUS.addListener(this::onCommandsRegister); + MinecraftForge.EVENT_BUS.addListener(this::doWorldTick); + MinecraftForge.EVENT_BUS.addListener(this::doRenderTick); + MinecraftForge.EVENT_BUS.addListener(this::onLogin); + MinecraftForge.EVENT_BUS.addListener(this::onWorldLoaded); + MinecraftForge.EVENT_BUS.addListener(this::onChangeDimension); + } + + private void onCommandsRegister(RegisterCommandsEvent event) { + new TimelordCommands(event.getDispatcher()); + ConfigCommand.register(event.getDispatcher()); + } + + private void doWorldTick(TickEvent.WorldTickEvent event) { + if (event.world.isRemote) return; + TimelordServer.worldTick(event); + } + + private void doRenderTick(TickEvent.RenderTickEvent event) { + TimelordClient.renderTick(event); + } + + private void onCommonSetupEvent(FMLCommonSetupEvent ezvent) { + if (!new File("./config/timelord/").exists()) { + if (!new File("./config/timelord/").mkdir()) { + System.out.println("Error creating Timelord folder, check permissions?"); + } else { + System.out.println("Successfully created Timelord folder"); + } + } + TIME_CHANNEL = NetworkRegistry.newSimpleChannel(new ResourceLocation("timelord", "timelord"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); + int index = 0; + TIME_CHANNEL.registerMessage(index++, TimePacket.class, TimePacket::encode, TimePacket::decode, TimePacket::handle, Optional.of(PLAY_TO_CLIENT)); + } + + private void onLogin(PlayerEvent.PlayerLoggedInEvent event) { + if (event.getPlayer().world.isRemote) return; + ResourceLocation rl = event.getPlayer().world.getDimensionKey().getLocation(); + TIME_CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()), new TimePacket(rl, Configuration.getConfigAsString(dimensionsConfigMap.get(rl)))); + } + + private void onChangeDimension(PlayerEvent.PlayerChangedDimensionEvent event) { + if (event.getPlayer().world.isRemote) return; + ResourceLocation rl = event.getPlayer().world.getDimensionKey().getLocation(); + TIME_CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()), new TimePacket(rl, Configuration.getConfigAsString(dimensionsConfigMap.get(rl)))); + } + + private void onWorldLoaded(WorldEvent.Load event) { + if (!event.getWorld().isRemote() && event.getWorld() instanceof ServerWorld) { + ServerWorld sw = (ServerWorld) event.getWorld(); + dimensionsConfigMap.put(sw.getDimensionKey().getLocation(), Configuration.init(sw)); + } + } +} diff --git a/src/main/java/stargazer/timelord/commands/TimelordCommands.java b/src/main/java/stargazer/timelord/commands/TimelordCommands.java new file mode 100644 index 0000000..6551bc7 --- /dev/null +++ b/src/main/java/stargazer/timelord/commands/TimelordCommands.java @@ -0,0 +1,39 @@ +package stargazer.timelord.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.fml.network.PacketDistributor; +import stargazer.timelord.Timelord; +import stargazer.timelord.config.Configuration; +import stargazer.timelord.net.TimePacket; + +import static stargazer.timelord.Timelord.getDimensionsConfigMap; + +public class TimelordCommands { + public TimelordCommands(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("timelord").then(Commands.literal("reload").requires(s -> s.hasPermissionLevel(4)).executes((command) -> { + return reload(command.getSource()); + }))); + } + + private int reload(CommandSource source) throws CommandSyntaxException { + + ServerWorld sw = source.getWorld(); + for (ServerWorld world : sw.getServer().getWorlds()) { + ResourceLocation rl = world.getDimensionKey().getLocation(); + getDimensionsConfigMap().put(rl, Configuration.init(world)); + Timelord.TIME_CHANNEL.send(PacketDistributor.ALL.noArg(), + new TimePacket(rl, Configuration.getConfigAsString(getDimensionsConfigMap().get(rl))) + ); + + source.sendFeedback(new StringTextComponent("Timelord config reloaded for " + world.getDimensionKey().getLocation()), true); + } + source.sendFeedback(new StringTextComponent("Timelord config succesfully reloaded!"), true); + return 1; + } +} diff --git a/src/main/java/stargazer/timelord/config/Configuration.java b/src/main/java/stargazer/timelord/config/Configuration.java new file mode 100644 index 0000000..5fdd1a0 --- /dev/null +++ b/src/main/java/stargazer/timelord/config/Configuration.java @@ -0,0 +1,47 @@ +package stargazer.timelord.config; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; +import net.minecraft.world.server.ServerWorld; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class Configuration { + private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + + public static DimensionTimeConfig initFromString(String string) { + return gson.fromJson(string, DimensionTimeConfig.class); + } + + public static DimensionTimeConfig init(ServerWorld sw) { + String filePath = String.format("./config/timelord/%s.json", sw.getDimensionKey().getLocation()).replace(":", "_"); + return loadOrCreateDimensionConfig(filePath); + } + + public static String getConfigAsString(DimensionTimeConfig timeConfig) { + return gson.toJson(timeConfig); + } + + + private static DimensionTimeConfig loadOrCreateDimensionConfig(String filePath) { + try { + JsonReader reader = new JsonReader(new FileReader(filePath)); + return gson.fromJson(reader, DimensionTimeConfig.class); + } catch (FileNotFoundException ex) { + DimensionTimeConfig cfg = new DimensionTimeConfig(); + try { + Writer writer = Files.newBufferedWriter(Paths.get(filePath)); + gson.toJson(cfg, writer); + writer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return cfg; + } + } + +} \ No newline at end of file diff --git a/src/main/java/stargazer/timelord/config/DimensionTimeConfig.java b/src/main/java/stargazer/timelord/config/DimensionTimeConfig.java new file mode 100644 index 0000000..e605da1 --- /dev/null +++ b/src/main/java/stargazer/timelord/config/DimensionTimeConfig.java @@ -0,0 +1,27 @@ +package stargazer.timelord.config; + +public class DimensionTimeConfig { + public boolean active = false; + + public Rain rain = new Rain(); + public Time time = new Time(); + + public static class Rain { + public boolean endlessRain = false; + public boolean endlessThunder = false; + public long income = 0; + public long outcome = 0; + public long thunder = 0; + public long min = 0; + public long max = 0; + public int dice = 0; + } + + public class Time { + public short hours_offset = 0; + public double scale = 0; + + public short fixedDayTime = -1; + + } +} \ No newline at end of file diff --git a/src/main/java/stargazer/timelord/logic/TimelordClient.java b/src/main/java/stargazer/timelord/logic/TimelordClient.java new file mode 100644 index 0000000..585d928 --- /dev/null +++ b/src/main/java/stargazer/timelord/logic/TimelordClient.java @@ -0,0 +1,62 @@ +package stargazer.timelord.logic; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.world.World; +import net.minecraft.world.storage.IWorldInfo; +import net.minecraftforge.event.TickEvent; +import stargazer.timelord.Timelord; +import stargazer.timelord.config.DimensionTimeConfig; + +import javax.annotation.Nullable; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; + +public class TimelordClient { + + @Nullable + private static DimensionTimeConfig getConfig(World world) { + return Timelord.getDimensionsConfigMap().get(world.getDimensionKey().getLocation()); + } + + public static void renderTick(TickEvent.RenderTickEvent event) { + if (event.phase != TickEvent.Phase.START) return; + final World world = Minecraft.getInstance().world; + if (world == null) return; + handleTime(world, getConfig(world)); + } + + + private static void handleTime(World world, DimensionTimeConfig cfg) { + if (cfg == null || cfg.time == null || !cfg.active) return; + + if (cfg.time.fixedDayTime <= 24000 && cfg.time.fixedDayTime >= 0) { + setFixedTime((ClientWorld) world, cfg.time.fixedDayTime); + } else if (cfg.time.scale > 0) { + setScaledTime((ClientWorld) world, cfg.time.scale); + } else if (cfg.time.hours_offset >= -18 && cfg.time.hours_offset <= 18) { + setRealTime((ClientWorld) world, ZoneOffset.ofTotalSeconds(cfg.time.hours_offset * 3600)); + } + } + + private static void setFixedTime(ClientWorld world, int fixedTime) { + world.setDayTime(fixedTime); + } + + private static void setRealTime(ClientWorld world, ZoneId timezone) { + final Duration mcTimeFix = Duration.ofHours(6); + final LocalDateTime now = LocalDateTime.now(timezone).minus(mcTimeFix); + + final long secs = now.toEpochSecond(ZoneOffset.UTC); + final long time = (secs * 24000) / 86400; // Convert from real day to minecraft day + world.setDayTime(time); + } + + private static void setScaledTime(ClientWorld world, double scale) { + final IWorldInfo wi = world.getWorldInfo(); + final long tt = wi.getGameTime(); + world.setDayTime((long) (tt * scale)); + } +} \ No newline at end of file diff --git a/src/main/java/stargazer/timelord/logic/TimelordServer.java b/src/main/java/stargazer/timelord/logic/TimelordServer.java new file mode 100644 index 0000000..eb65f80 --- /dev/null +++ b/src/main/java/stargazer/timelord/logic/TimelordServer.java @@ -0,0 +1,107 @@ +package stargazer.timelord.logic; + +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; +import net.minecraft.world.storage.IServerWorldInfo; +import net.minecraft.world.storage.IWorldInfo; +import net.minecraftforge.event.TickEvent; +import stargazer.timelord.Timelord; +import stargazer.timelord.config.DimensionTimeConfig; + +import javax.annotation.Nullable; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; + +public class TimelordServer { + + @Nullable + private static DimensionTimeConfig getConfig(World world) { + return Timelord.getDimensionsConfigMap().get(world.getDimensionKey().getLocation()); + } + + public static void worldTick(TickEvent.WorldTickEvent event) { + if (event.phase != TickEvent.Phase.END) return; + final DimensionTimeConfig cfg = getConfig(event.world); + if (cfg == null) return; + if (!event.world.isRemote() && event.world instanceof ServerWorld) { + final TimelordWorldData data = TimelordWorldData.getForWorld((ServerWorld) event.world); + handleRain((ServerWorld) event.world, cfg, data); + } + handleTime(event.world, cfg); + } + + private static void handleTime(World world, DimensionTimeConfig cfg) { + if (cfg == null || cfg.time == null || !cfg.active) return; + + if (cfg.time.fixedDayTime <= 24000 && cfg.time.fixedDayTime >= 0) { + setFixedTime((ServerWorld) world, cfg.time.fixedDayTime); + } else if (cfg.time.scale > 0) { + setScaledTime((ServerWorld) world, cfg.time.scale); + } else if (cfg.time.hours_offset >= -18 && cfg.time.hours_offset <= 18) { + setRealTime((ServerWorld) world, ZoneOffset.ofTotalSeconds(cfg.time.hours_offset * 3600)); + } + } + + private static void setFixedTime(ServerWorld world, int fixedTime) { + world.setDayTime(fixedTime); + } + + private static void setRealTime(ServerWorld world, ZoneId timezone) { + final Duration mcTimeFix = Duration.ofHours(6); + final LocalDateTime now = LocalDateTime.now(timezone).minus(mcTimeFix); + + final long secs = now.toEpochSecond(ZoneOffset.UTC); + final long time = (secs * 24000) / 86400; // Convert from real day to minecraft day + world.setDayTime(time); + } + + private static void setScaledTime(ServerWorld world, double scale) { + final IWorldInfo wi = world.getWorldInfo(); + final long tt = wi.getGameTime(); + world.setDayTime((long) (tt * scale)); + } + + private static void handleRain(ServerWorld world, DimensionTimeConfig cfg, TimelordWorldData data) { + if (cfg == null || cfg.rain == null || !cfg.active) return; + + final IServerWorldInfo wi = (IServerWorldInfo) world.getWorldInfo(); + + wi.setRainTime(Integer.MAX_VALUE); + wi.setThunderTime(Integer.MAX_VALUE); + wi.setClearWeatherTime(0); + + if (cfg.rain.endlessRain && wi.isRaining()) return; + if (cfg.rain.endlessThunder && wi.isThundering()) return; + + if (cfg.rain.endlessRain) { + world.getWorldInfo().setRaining(true); + } else if (cfg.rain.endlessThunder) { + wi.setThundering(true); + } else { + if (wi.isRaining()) { + data.rainAcc -= cfg.rain.outcome; + if (data.rainAcc <= 0) { + System.out.println("Timelord: succesfull rain stop"); + world.getWorldInfo().setRaining(false); + wi.setThundering(false); + } + } else { + if (cfg.rain.income == 0) return; + + data.rainAcc += cfg.rain.income; + final boolean shouldRoll = data.rainAcc >= cfg.rain.min && cfg.rain.dice > 0; + final boolean roll = shouldRoll && world.rand.nextInt(cfg.rain.dice) == 0; + if (roll) { + System.out.printf("Timelord: [%s] successful rainfall dice roll (while %d>=%d)%n", + wi.getWorldName(), data.rainAcc, cfg.rain.min); + } + if (roll || data.rainAcc > cfg.rain.max) { + world.getWorldInfo().setRaining(true); + wi.setThundering(data.rainAcc > cfg.rain.thunder); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/stargazer/timelord/logic/TimelordWorldData.java b/src/main/java/stargazer/timelord/logic/TimelordWorldData.java new file mode 100644 index 0000000..a3d4429 --- /dev/null +++ b/src/main/java/stargazer/timelord/logic/TimelordWorldData.java @@ -0,0 +1,58 @@ +package stargazer.timelord.logic; + +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.world.server.ServerWorld; +import net.minecraft.world.storage.DimensionSavedDataManager; +import net.minecraft.world.storage.WorldSavedData; +import stargazer.timelord.Timelord; + +import java.util.function.Supplier; + + +public class TimelordWorldData extends WorldSavedData implements Supplier { + + public TimelordWorldData() { + super(DATA_NAME); + } + + public TimelordWorldData(String name) { + super(name); + } + + private static final String DATA_NAME = Timelord.MODID + ".timedata"; + + long rainAcc = 0; + + public static TimelordWorldData getForWorld(ServerWorld world) { + final DimensionSavedDataManager storage = world.getSavedData(); + Supplier supplier = new TimelordWorldData(); + TimelordWorldData instance = storage.getOrCreate(supplier, DATA_NAME); + if (instance == null) { + instance = new TimelordWorldData(); + storage.set(instance); + } + return instance; + } + + + @Override + public void read(CompoundNBT nbt) { + rainAcc = nbt.getLong("rain"); + } + + @Override + public CompoundNBT write(CompoundNBT compound) { + compound.putLong("rain", rainAcc); + return compound; + } + + @Override + public boolean isDirty() { + return true; + } + + @Override + public Object get() { + return this; + } +} \ No newline at end of file diff --git a/src/main/java/stargazer/timelord/net/TimePacket.java b/src/main/java/stargazer/timelord/net/TimePacket.java new file mode 100644 index 0000000..8108e95 --- /dev/null +++ b/src/main/java/stargazer/timelord/net/TimePacket.java @@ -0,0 +1,44 @@ +package stargazer.timelord.net; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.network.NetworkEvent; +import stargazer.timelord.Timelord; + +import java.util.function.Supplier; + +public class TimePacket { + + private final ResourceLocation worldResourceLocation; + private final String stringedJson; + + public TimePacket(ResourceLocation worldResourceLocation, final String stringedJson) { + this.worldResourceLocation = worldResourceLocation; + this.stringedJson = stringedJson; + } + + public static void encode(final TimePacket packet, final PacketBuffer buf) { + buf.writeResourceLocation(packet.worldResourceLocation); + buf.writeString(packet.stringedJson); + + } + + public static TimePacket decode(final PacketBuffer buf) { + return new TimePacket(buf.readResourceLocation(), buf.readString()); + } + + public static void handle(TimePacket msg, Supplier ctx) { + NetworkEvent.Context context = ctx.get(); + context.enqueueWork(() -> DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> clientHandler(msg))); + ctx.get().setPacketHandled(true); + } + + @OnlyIn(Dist.CLIENT) + private static void clientHandler(final TimePacket message) { + Timelord.putStringIntoDimensionsConfigMap(message.worldResourceLocation, message.stringedJson); + + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..9505d0a --- /dev/null +++ b/src/main/resources/META-INF/mods.toml @@ -0,0 +1,58 @@ +# This is an example mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader="javafml" #mandatory +# A version range to match for said mod loader - for regular FML @Mod it will be the forge version +loaderVersion="[36,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. +# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license="All rights reserved" +# A URL to refer people to when problems occur with this mod +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory +# The modid of the mod +modId="timelord" #mandatory +# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it +# ${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata +# see the associated build.gradle script for how to populate this completely automatically during a build +version="${file.jarVersion}" #mandatory + # A display name for the mod +displayName="Timelord" #mandatory +# A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/ +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional +# A URL for the "homepage" for this mod, displayed in the mod UI +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional +# A file name (in the root of the mod JAR) containing a logo for display +logoFile="examplemod.png" #optional +# A text field displayed in the mod UI +credits="Thanks to Msifeed for most of the mod logic" #optional +# A text field displayed in the mod UI +authors="Marduk, Stargazer Roleplay Project" #optional +# The description text for the mod (multi line!) (#mandatory) +description=''' +Timelord allows you to change time rules per dimension. Also affects rain. +''' +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies.timelord]] #optional + # the modid of the dependency + modId="forge" #mandatory + # Does this dependency have to exist - if not, ordering below must be specified + mandatory=true #mandatory + # The version range of the dependency + versionRange="[36,)" #mandatory + # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT or SERVER + side="BOTH" +# Here's another dependency +[[dependencies.timelord]] + modId="minecraft" + mandatory=true +# This version range declares a minimum of the current minecraft version up to but not including the next major version + versionRange="[1.16.5,1.17)" + ordering="NONE" + side="BOTH" diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta new file mode 100644 index 0000000..c79a362 --- /dev/null +++ b/src/main/resources/pack.mcmeta @@ -0,0 +1,7 @@ +{ + "pack": { + "description": "examplemod resources", + "pack_format": 6, + "_comment": "A pack_format of 6 requires json lang files and some texture changes from 1.16.2. Note: we require v6 pack meta for all mods." + } +}