├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── phar-build.yml ├── .gitignore ├── .libraries.yml ├── .poggit.yml ├── LICENSE ├── README.md ├── assets └── logo-min.png ├── generatepets.php ├── plugin.yml ├── resources ├── config.yml ├── mysql.sql └── sqlite.sql └── src └── brokiem └── simplepets ├── EventListener.php ├── SimplePets.php ├── command └── Command.php ├── database └── Database.php ├── manager └── PetManager.php └── pets ├── AllayPet.php ├── ArmorstandPet.php ├── ArrowPet.php ├── AxolotlPet.php ├── BatPet.php ├── BeePet.php ├── BlazePet.php ├── BoatPet.php ├── CatPet.php ├── CavespiderPet.php ├── ChestminecartPet.php ├── ChickenPet.php ├── CodPet.php ├── CommandblockminecartPet.php ├── CowPet.php ├── CreeperPet.php ├── DolphinPet.php ├── DonkeyPet.php ├── DragonfireballPet.php ├── DrownedPet.php ├── EggPet.php ├── ElderguardianPet.php ├── ElderguardianghostPet.php ├── EndercrystalPet.php ├── EnderdragonPet.php ├── EndermanPet.php ├── EndermitePet.php ├── EnderpearlPet.php ├── EvocationfangPet.php ├── EvocationillagerPet.php ├── EyeofendersignalPet.php ├── FireballPet.php ├── FoxPet.php ├── GhastPet.php ├── GoatPet.php ├── GuardianPet.php ├── HoglinPet.php ├── HopperminecartPet.php ├── HorsePet.php ├── HuskPet.php ├── IrongolemPet.php ├── LingeringpotionPet.php ├── LlamaPet.php ├── MagmacubePet.php ├── MinecartPet.php ├── MooshroomPet.php ├── MulePet.php ├── OcelotPet.php ├── PandaPet.php ├── ParrotPet.php ├── PhantomPet.php ├── PigPet.php ├── PiglinPet.php ├── PillagerPet.php ├── PolarbearPet.php ├── PufferfishPet.php ├── RabbitPet.php ├── RavagerPet.php ├── SalmonPet.php ├── SheepPet.php ├── ShulkerPet.php ├── ShulkerbulletPet.php ├── SilverfishPet.php ├── SkeletonPet.php ├── SkeletonhorsePet.php ├── SlimePet.php ├── SmallfireballPet.php ├── SnowballPet.php ├── SnowgolemPet.php ├── SpiderPet.php ├── SplashpotionPet.php ├── SquidPet.php ├── StrayPet.php ├── StriderPet.php ├── TntPet.php ├── TntminecartPet.php ├── TropicalfishPet.php ├── TurtlePet.php ├── VexPet.php ├── VillagerPet.php ├── Villagerv2Pet.php ├── VindicatorPet.php ├── WanderingtraderPet.php ├── WardenPet.php ├── WitchPet.php ├── WitherPet.php ├── WitherskeletonPet.php ├── WitherskullPet.php ├── WolfPet.php ├── ZoglinPet.php ├── ZombiePet.php ├── ZombiehorsePet.php ├── ZombiepigmanPet.php ├── ZombievillagerPet.php ├── Zombievillagerv2Pet.php └── base ├── BasePet.php └── CustomPet.php /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = crlf 4 | indent_size = 4 5 | indent_style = space 6 | insert_final_newline = false 7 | max_line_length = 120 8 | tab_width = 4 9 | ij_continuation_indent_size = 8 10 | ij_formatter_off_tag = @formatter:off 11 | ij_formatter_on_tag = @formatter:on 12 | ij_formatter_tags_enabled = false 13 | ij_smart_tabs = false 14 | ij_visual_guides = none 15 | ij_wrap_on_typing = false 16 | 17 | [.editorconfig] 18 | ij_editorconfig_align_group_field_declarations = false 19 | ij_editorconfig_space_after_colon = false 20 | ij_editorconfig_space_after_comma = true 21 | ij_editorconfig_space_before_colon = false 22 | ij_editorconfig_space_before_comma = false 23 | ij_editorconfig_spaces_around_assignment_operators = true 24 | 25 | [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] 26 | ij_xml_align_attributes = true 27 | ij_xml_align_text = false 28 | ij_xml_attribute_wrap = normal 29 | ij_xml_block_comment_at_first_column = true 30 | ij_xml_keep_blank_lines = 2 31 | ij_xml_keep_indents_on_empty_lines = false 32 | ij_xml_keep_line_breaks = true 33 | ij_xml_keep_line_breaks_in_text = true 34 | ij_xml_keep_whitespaces = false 35 | ij_xml_keep_whitespaces_around_cdata = preserve 36 | ij_xml_keep_whitespaces_inside_cdata = false 37 | ij_xml_line_comment_at_first_column = true 38 | ij_xml_space_after_tag_name = false 39 | ij_xml_space_around_equals_in_attribute = false 40 | ij_xml_space_inside_empty_tag = false 41 | ij_xml_text_wrap = normal 42 | 43 | [{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}] 44 | ij_continuation_indent_size = 4 45 | ij_php_align_assignments = false 46 | ij_php_align_class_constants = false 47 | ij_php_align_group_field_declarations = false 48 | ij_php_align_inline_comments = false 49 | ij_php_align_key_value_pairs = false 50 | ij_php_align_multiline_array_initializer_expression = false 51 | ij_php_align_multiline_binary_operation = false 52 | ij_php_align_multiline_chained_methods = false 53 | ij_php_align_multiline_extends_list = false 54 | ij_php_align_multiline_for = true 55 | ij_php_align_multiline_parameters = true 56 | ij_php_align_multiline_parameters_in_calls = false 57 | ij_php_align_multiline_ternary_operation = false 58 | ij_php_align_phpdoc_comments = false 59 | ij_php_align_phpdoc_param_names = false 60 | ij_php_anonymous_brace_style = end_of_line 61 | ij_php_api_weight = 28 62 | ij_php_array_initializer_new_line_after_left_brace = false 63 | ij_php_array_initializer_right_brace_on_new_line = false 64 | ij_php_array_initializer_wrap = off 65 | ij_php_assignment_wrap = off 66 | ij_php_attributes_wrap = off 67 | ij_php_author_weight = 28 68 | ij_php_binary_operation_sign_on_next_line = false 69 | ij_php_binary_operation_wrap = off 70 | ij_php_blank_lines_after_class_header = 0 71 | ij_php_blank_lines_after_function = 1 72 | ij_php_blank_lines_after_imports = 1 73 | ij_php_blank_lines_after_opening_tag = 0 74 | ij_php_blank_lines_after_package = 0 75 | ij_php_blank_lines_around_class = 1 76 | ij_php_blank_lines_around_constants = 0 77 | ij_php_blank_lines_around_field = 0 78 | ij_php_blank_lines_around_method = 1 79 | ij_php_blank_lines_before_class_end = 0 80 | ij_php_blank_lines_before_imports = 1 81 | ij_php_blank_lines_before_method_body = 0 82 | ij_php_blank_lines_before_package = 1 83 | ij_php_blank_lines_before_return_statement = 0 84 | ij_php_blank_lines_between_imports = 0 85 | ij_php_block_brace_style = end_of_line 86 | ij_php_call_parameters_new_line_after_left_paren = false 87 | ij_php_call_parameters_right_paren_on_new_line = false 88 | ij_php_call_parameters_wrap = off 89 | ij_php_catch_on_new_line = false 90 | ij_php_category_weight = 28 91 | ij_php_class_brace_style = end_of_line 92 | ij_php_comma_after_last_array_element = false 93 | ij_php_concat_spaces = true 94 | ij_php_copyright_weight = 28 95 | ij_php_deprecated_weight = 28 96 | ij_php_do_while_brace_force = never 97 | ij_php_else_if_style = as_is 98 | ij_php_else_on_new_line = false 99 | ij_php_example_weight = 28 100 | ij_php_extends_keyword_wrap = off 101 | ij_php_extends_list_wrap = off 102 | ij_php_fields_default_visibility = private 103 | ij_php_filesource_weight = 28 104 | ij_php_finally_on_new_line = false 105 | ij_php_for_brace_force = never 106 | ij_php_for_statement_new_line_after_left_paren = false 107 | ij_php_for_statement_right_paren_on_new_line = false 108 | ij_php_for_statement_wrap = off 109 | ij_php_force_short_declaration_array_style = false 110 | ij_php_getters_setters_naming_style = camel_case 111 | ij_php_getters_setters_order_style = getters_first 112 | ij_php_global_weight = 28 113 | ij_php_group_use_wrap = on_every_item 114 | ij_php_if_brace_force = never 115 | ij_php_if_lparen_on_next_line = false 116 | ij_php_if_rparen_on_next_line = false 117 | ij_php_ignore_weight = 28 118 | ij_php_import_sorting = alphabetic 119 | ij_php_indent_break_from_case = true 120 | ij_php_indent_case_from_switch = true 121 | ij_php_indent_code_in_php_tags = false 122 | ij_php_internal_weight = 28 123 | ij_php_keep_blank_lines_after_lbrace = 2 124 | ij_php_keep_blank_lines_before_right_brace = 2 125 | ij_php_keep_blank_lines_in_code = 2 126 | ij_php_keep_blank_lines_in_declarations = 2 127 | ij_php_keep_control_statement_in_one_line = true 128 | ij_php_keep_first_column_comment = false 129 | ij_php_keep_indents_on_empty_lines = false 130 | ij_php_keep_line_breaks = true 131 | ij_php_keep_rparen_and_lbrace_on_one_line = false 132 | ij_php_keep_simple_classes_in_one_line = true 133 | ij_php_keep_simple_methods_in_one_line = true 134 | ij_php_lambda_brace_style = end_of_line 135 | ij_php_license_weight = 28 136 | ij_php_line_comment_add_space = false 137 | ij_php_line_comment_at_first_column = true 138 | ij_php_link_weight = 28 139 | ij_php_lower_case_boolean_const = false 140 | ij_php_lower_case_keywords = true 141 | ij_php_lower_case_null_const = false 142 | ij_php_method_brace_style = end_of_line 143 | ij_php_method_call_chain_wrap = off 144 | ij_php_method_parameters_new_line_after_left_paren = false 145 | ij_php_method_parameters_right_paren_on_new_line = false 146 | ij_php_method_parameters_wrap = off 147 | ij_php_method_weight = 28 148 | ij_php_modifier_list_wrap = false 149 | ij_php_multiline_chained_calls_semicolon_on_new_line = false 150 | ij_php_namespace_brace_style = 1 151 | ij_php_new_line_after_php_opening_tag = false 152 | ij_php_null_type_position = in_the_end 153 | ij_php_package_weight = 28 154 | ij_php_param_weight = 0 155 | ij_php_parameters_attributes_wrap = off 156 | ij_php_parentheses_expression_new_line_after_left_paren = false 157 | ij_php_parentheses_expression_right_paren_on_new_line = false 158 | ij_php_phpdoc_blank_line_before_tags = false 159 | ij_php_phpdoc_blank_lines_around_parameters = false 160 | ij_php_phpdoc_keep_blank_lines = true 161 | ij_php_phpdoc_param_spaces_between_name_and_description = 1 162 | ij_php_phpdoc_param_spaces_between_tag_and_type = 1 163 | ij_php_phpdoc_param_spaces_between_type_and_name = 1 164 | ij_php_phpdoc_use_fqcn = false 165 | ij_php_phpdoc_wrap_long_lines = false 166 | ij_php_place_assignment_sign_on_next_line = false 167 | ij_php_place_parens_for_constructor = 0 168 | ij_php_property_read_weight = 28 169 | ij_php_property_weight = 28 170 | ij_php_property_write_weight = 28 171 | ij_php_return_type_on_new_line = false 172 | ij_php_return_weight = 1 173 | ij_php_see_weight = 28 174 | ij_php_since_weight = 28 175 | ij_php_sort_phpdoc_elements = true 176 | ij_php_space_after_colon = true 177 | ij_php_space_after_colon_in_named_argument = true 178 | ij_php_space_after_colon_in_return_type = true 179 | ij_php_space_after_comma = true 180 | ij_php_space_after_for_semicolon = true 181 | ij_php_space_after_quest = true 182 | ij_php_space_after_type_cast = false 183 | ij_php_space_after_unary_not = false 184 | ij_php_space_before_array_initializer_left_brace = false 185 | ij_php_space_before_catch_keyword = true 186 | ij_php_space_before_catch_left_brace = true 187 | ij_php_space_before_catch_parentheses = true 188 | ij_php_space_before_class_left_brace = true 189 | ij_php_space_before_closure_left_parenthesis = false 190 | ij_php_space_before_colon = true 191 | ij_php_space_before_colon_in_named_argument = false 192 | ij_php_space_before_colon_in_return_type = false 193 | ij_php_space_before_comma = false 194 | ij_php_space_before_do_left_brace = true 195 | ij_php_space_before_else_keyword = true 196 | ij_php_space_before_else_left_brace = true 197 | ij_php_space_before_finally_keyword = true 198 | ij_php_space_before_finally_left_brace = true 199 | ij_php_space_before_for_left_brace = true 200 | ij_php_space_before_for_parentheses = true 201 | ij_php_space_before_for_semicolon = false 202 | ij_php_space_before_if_left_brace = true 203 | ij_php_space_before_if_parentheses = true 204 | ij_php_space_before_method_call_parentheses = false 205 | ij_php_space_before_method_left_brace = true 206 | ij_php_space_before_method_parentheses = false 207 | ij_php_space_before_quest = true 208 | ij_php_space_before_short_closure_left_parenthesis = false 209 | ij_php_space_before_switch_left_brace = true 210 | ij_php_space_before_switch_parentheses = true 211 | ij_php_space_before_try_left_brace = true 212 | ij_php_space_before_unary_not = false 213 | ij_php_space_before_while_keyword = true 214 | ij_php_space_before_while_left_brace = true 215 | ij_php_space_before_while_parentheses = true 216 | ij_php_space_between_ternary_quest_and_colon = false 217 | ij_php_spaces_around_additive_operators = true 218 | ij_php_spaces_around_arrow = false 219 | ij_php_spaces_around_assignment_in_declare = false 220 | ij_php_spaces_around_assignment_operators = true 221 | ij_php_spaces_around_bitwise_operators = true 222 | ij_php_spaces_around_equality_operators = true 223 | ij_php_spaces_around_logical_operators = true 224 | ij_php_spaces_around_multiplicative_operators = true 225 | ij_php_spaces_around_null_coalesce_operator = true 226 | ij_php_spaces_around_relational_operators = true 227 | ij_php_spaces_around_shift_operators = true 228 | ij_php_spaces_around_unary_operator = false 229 | ij_php_spaces_around_var_within_brackets = false 230 | ij_php_spaces_within_array_initializer_braces = false 231 | ij_php_spaces_within_brackets = false 232 | ij_php_spaces_within_catch_parentheses = false 233 | ij_php_spaces_within_for_parentheses = false 234 | ij_php_spaces_within_if_parentheses = false 235 | ij_php_spaces_within_method_call_parentheses = false 236 | ij_php_spaces_within_method_parentheses = false 237 | ij_php_spaces_within_parentheses = false 238 | ij_php_spaces_within_short_echo_tags = true 239 | ij_php_spaces_within_switch_parentheses = false 240 | ij_php_spaces_within_while_parentheses = false 241 | ij_php_special_else_if_treatment = false 242 | ij_php_subpackage_weight = 28 243 | ij_php_ternary_operation_signs_on_next_line = false 244 | ij_php_ternary_operation_wrap = off 245 | ij_php_throws_weight = 2 246 | ij_php_todo_weight = 28 247 | ij_php_unknown_tag_weight = 28 248 | ij_php_upper_case_boolean_const = false 249 | ij_php_upper_case_null_const = false 250 | ij_php_uses_weight = 28 251 | ij_php_var_weight = 28 252 | ij_php_variable_naming_style = mixed 253 | ij_php_version_weight = 28 254 | ij_php_while_brace_force = never 255 | ij_php_while_on_new_line = false 256 | 257 | [{*.markdown,*.md}] 258 | ij_markdown_force_one_space_after_blockquote_symbol = true 259 | ij_markdown_force_one_space_after_header_symbol = true 260 | ij_markdown_force_one_space_after_list_bullet = true 261 | ij_markdown_force_one_space_between_words = true 262 | ij_markdown_keep_indents_on_empty_lines = false 263 | ij_markdown_max_lines_around_block_elements = 1 264 | ij_markdown_max_lines_around_header = 1 265 | ij_markdown_max_lines_between_paragraphs = 1 266 | ij_markdown_min_lines_around_block_elements = 1 267 | ij_markdown_min_lines_around_header = 1 268 | ij_markdown_min_lines_between_paragraphs = 1 269 | 270 | [{*.yaml,*.yml}] 271 | indent_size = 2 272 | ij_yaml_align_values_properties = do_not_align 273 | ij_yaml_autoinsert_sequence_marker = true 274 | ij_yaml_block_mapping_on_new_line = false 275 | ij_yaml_indent_sequence_value = true 276 | ij_yaml_keep_indents_on_empty_lines = false 277 | ij_yaml_keep_line_breaks = true 278 | ij_yaml_sequence_on_new_line = false 279 | ij_yaml_space_before_colon = false 280 | ij_yaml_spaces_within_braces = true 281 | ij_yaml_spaces_within_brackets = true 282 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: brokiem 2 | custom: [ 'https://www.paypal.me/brokiem' ] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **SimplePets plugin information** 28 | 29 | - Version: [e.g. 1.0.0] 30 | - Server api: [e.g. 4.0.1] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/phar-build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | build: 7 | name: Build plugin 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | 12 | - name: Prepare directories 13 | run: | 14 | mkdir target 15 | mkdir plugin 16 | 17 | - name: Move files to plugin folder 18 | run: | 19 | mv resources src plugin.yml -t plugin 20 | 21 | - name: Setup PHP 8.1 22 | uses: shivammathur/setup-php@v2 23 | with: 24 | php-version: '8.1' 25 | ini-values: phar.readonly=0 26 | 27 | - name: Read plugin name 28 | id: plugin-name 29 | uses: KJ002/read-yaml@main 30 | with: 31 | file: './plugin/plugin.yml' 32 | key-path: '["name"]' 33 | 34 | - name: Download ConsoleScript 35 | run: wget https://gist.githubusercontent.com/brokiem/3a407df6372ed1410cfb38cf99762a3f/raw/3f7e65b169a13bff5f2ec91669f9b474cf25a6cc/ConsoleScript.php 36 | 37 | - name: Build plugin 38 | run: php ConsoleScript.php --make / --relative plugin --out target/${{ steps.plugin-name.outputs.data }}.phar 39 | 40 | - name: Check libraries file 41 | id: libraries-file 42 | uses: andstor/file-existence-action@v1 43 | with: 44 | files: ".libraries.yml" 45 | 46 | - name: Read libraries 47 | if: steps.libraries-file.outputs.files_exists == 'true' 48 | id: libraries 49 | uses: KJ002/read-yaml@main 50 | with: 51 | file: './.libraries.yml' 52 | key-path: '["libs"]' 53 | 54 | - name: Download virions 55 | if: steps.libraries-file.outputs.files_exists == 'true' 56 | run: php -r 'foreach(${{ steps.libraries.outputs.data }} as $lib){ exec("wget $lib"); echo "Downloaded virion " . basename($lib); }' 57 | 58 | - name: Inject virions 59 | if: steps.libraries-file.outputs.files_exists == 'true' 60 | run: php -r 'foreach(${{ steps.libraries.outputs.data }} as $lib){ exec("php " . basename($lib) . " target/${{ steps.plugin-name.outputs.data }}.phar"); echo "\nInjected virion " . basename($lib); }' 61 | 62 | - name: Compress phar 63 | run: php -r '(new \Phar("./target/${{ steps.plugin-name.outputs.data }}.phar"))->compressFiles(\Phar::GZ); echo "\nFinished compression";' 64 | 65 | - name: Upload Artifact 66 | uses: actions/upload-artifact@v2 67 | with: 68 | name: ${{ steps.plugin-name.outputs.data }}.phar 69 | path: target/${{ steps.plugin-name.outputs.data }}.phar 70 | 71 | prepare-release: 72 | name: Make release 73 | needs: [ build ] 74 | runs-on: ubuntu-latest 75 | if: contains(github.event.head_commit.message, '[release]') 76 | steps: 77 | - uses: actions/checkout@v2 78 | 79 | - name: Download artifact 80 | uses: actions/download-artifact@v2 81 | 82 | - name: Read plugin name 83 | id: plugin-name 84 | uses: KJ002/read-yaml@main 85 | with: 86 | file: './plugin.yml' 87 | key-path: '["name"]' 88 | 89 | - name: Read plugin version 90 | id: plugin-version 91 | uses: KJ002/read-yaml@main 92 | with: 93 | file: './plugin.yml' 94 | key-path: '["version"]' 95 | 96 | - name: Create and publish release 97 | uses: marvinpinto/action-automatic-releases@v1.2.1 98 | with: 99 | automatic_release_tag: "${{ steps.plugin-version.outputs.data }}" 100 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 101 | title: "${{ steps.plugin-name.outputs.data }} v${{ steps.plugin-version.outputs.data }}" 102 | files: | 103 | ${{ steps.plugin-name.outputs.data }}.phar 104 | prerelease: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor -------------------------------------------------------------------------------- /.libraries.yml: -------------------------------------------------------------------------------- 1 | libs: 2 | - https://poggit.pmmp.io/r/177279/libasynql_dev-177.phar 3 | - https://poggit.pmmp.io/r/186359/InvMenu_dev-179.phar 4 | - https://poggit.pmmp.io/r/188653/libEasyUI_dev-12.phar -------------------------------------------------------------------------------- /.poggit.yml: -------------------------------------------------------------------------------- 1 | --- # Poggit-CI Manifest. Open the CI at https://poggit.pmmp.io/ci/brokiem/SimplePets 2 | build-by-default: true 3 | branches: 4 | - master 5 | - broki-patch 6 | 7 | projects: 8 | SimplePets: 9 | icon: "/assets/logo-min.png" 10 | libs: 11 | - src: poggit/libasynql/libasynql 12 | version: ^4.1.0 13 | - src: muqsit/InvMenu/InvMenu 14 | version: ^4.4.1 15 | branch: "4.0" 16 | - src: brokiem-pm-pl/EasyUI/libEasyUI 17 | version: 3.0.0 18 | ... 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 broki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

SimplePets


2 | 3 | An Ultimate Pets plugin made by broki for PocketMine-MP.
4 | [![License](https://img.shields.io/github/license/brokiem/SimplePets)](https://github.com/brokiem/SimplePets) 5 | [![Star](https://img.shields.io/github/stars/brokiem/SimplePets)](https://github.com/brokiem/SimplePets/stargazers) 6 | [![Discord](https://img.shields.io/discord/830063409000087612?color=7389D8&label=discord)](https://discord.com/invite/jy6abSrjhQ) 7 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/brokiem) 8 | 9 | ## ✨ Features 10 | 11 | - MySQL and SQLite database 12 | - Anti sql injection 13 | - Pets with inventory 14 | - Pets can be ridden 15 | - XUID data saving 16 | - Multi world support 17 | - Lightweight & Open Source ❤ 18 | 19 | ## 💬 Commands 20 | 21 | For more command info, please look directly at ```/spet help```
22 | 23 | | Command | Description | Permission | Default | 24 | | --- | --- | --- | --- | 25 | | ```/spet``` | ```SimplePets command list``` | ```none``` | ```true``` | 26 | | ```/spet petlist``` | ```Show all pet list``` | ```simplepets.petlist``` | ```op``` | 27 | | ```/spet spawn``` | ```Spawning a pet``` | ```simplepets.spawn``` | ```op``` | 28 | | ```/spet remove``` | ```Removing a pet``` | ```simplepets.remove``` | ```op``` | 29 | | ```/spet inv``` | ```View pet inventory``` | ```simplepets.inv``` | ```true``` | 30 | | ```/spet ride``` | ```Ride a pet``` | ```simplepets.ride``` | ```true``` | 31 | 32 | ## 📝 Todo List 33 | 34 | - UI/Form to manage pets 35 | - Renaming pets 36 | 37 | ## ❔ Issues 38 | 39 | Did you find a bug or error when using this plugin? feel free to open the 40 | issues [here](https://github.com/brokiem/SimplePets/issues/new) 41 | 42 | ## 👑 Donation 43 | 44 | Paypal - https://www.paypal.me/brokiem
45 | Saweria - https://saweria.co/brokiem 46 | -------------------------------------------------------------------------------- /assets/logo-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brokiem/SimplePets/0d0e8e21278840eb843209a8214d15c92fae7c53/assets/logo-min.png -------------------------------------------------------------------------------- /generatepets.php: -------------------------------------------------------------------------------- 1 | $value) { 110 | $const = ucfirst(strtolower(str_replace("_", "", $constant))) . "Pet"; 111 | 112 | $contents = "getConstants(); 129 | } 130 | 131 | public static function getDefaultPets(): void { 132 | $text = ""; 133 | foreach (glob('src/brokiem/simplepets/pets/*.php') as $file) { 134 | $text .= "\"" . str_replace(".php", "", basename($file)) . "\" => \"" . str_replace("/", "\\", str_replace(".php", "", $file)) . "\",\n"; 135 | } 136 | 137 | var_dump($text); 138 | } 139 | } 140 | 141 | generatepets::main(); -------------------------------------------------------------------------------- /plugin.yml: -------------------------------------------------------------------------------- 1 | name: SimplePets 2 | main: brokiem\simplepets\SimplePets 3 | version: 1.0.0-beta 4 | api: 4.0.0 5 | authors: [ "broki", "brokiem", "broski" ] 6 | 7 | website: https://github.com/brokiem/SimplePets 8 | 9 | extensions: 10 | Core: '>=8.0' 11 | 12 | permissions: 13 | simplepets.ui: 14 | default: op 15 | simplepets.spawn: 16 | default: op 17 | simplepets.remove: 18 | default: op 19 | simplepets.inv: 20 | default: true 21 | simplepets.ride: 22 | default: true 23 | simplepets.petlist: 24 | default: op -------------------------------------------------------------------------------- /resources/config.yml: -------------------------------------------------------------------------------- 1 | # Database configuration 2 | database: 3 | # The database types. "sqlite" and "mysql" are supported. 4 | type: sqlite 5 | 6 | # Edit these settings only if you choose "sqlite". 7 | sqlite: 8 | # The file name of the database in the plugin data folder. 9 | # You can also put an absolute path here. 10 | file: simplepets.sqlite 11 | 12 | # Edit these settings only if you choose "mysql". 13 | mysql: 14 | host: 127.0.0.1 15 | # Avoid using the "root" user for security reasons. 16 | username: root 17 | password: "" 18 | schema: simplepets # database 19 | 20 | # The maximum number of simultaneous SQL queries 21 | # Recommended: 1 for sqlite, 2 for MySQL. 22 | # You may want to further increase this value if your MySQL connection is very slow. 23 | worker-limit: 1 24 | 25 | enable-inventory: true 26 | enable-riding: true 27 | -------------------------------------------------------------------------------- /resources/mysql.sql: -------------------------------------------------------------------------------- 1 | -- # !mysql 2 | -- # {simplepets 3 | -- # {init 4 | -- # {info 5 | CREATE TABLE IF NOT EXISTS simplepets_info 6 | ( 7 | id TINYINT UNSIGNED PRIMARY KEY, 8 | db_version TINYINT UNSIGNED NOT NULL DEFAULT 1 9 | ); 10 | -- # } 11 | -- # {data 12 | CREATE TABLE IF NOT EXISTS simplepets_pets 13 | ( 14 | id INT UNSIGNED AUTO_INCREMENT, 15 | petType VARCHAR(64) NOT NULL, 16 | petName VARCHAR(32) NOT NULL, 17 | petOwner VARCHAR(32) NOT NULL, 18 | petSize FLOAT(2) NOT NULL, 19 | petBaby BOOL NOT NULL, 20 | petVisible INT NOT NULL, 21 | enableInv BOOL NOT NULL, 22 | enableRiding BOOL NOT NULL, 23 | extraData TEXT, 24 | PRIMARY KEY (id) 25 | ) 26 | -- # } 27 | -- # } 28 | -- # {get-version 29 | -- # :id int 1 30 | SELECT db_version 31 | FROM simplepets_info 32 | WHERE id = :id; 33 | -- # } 34 | -- # {set-version 35 | -- # :id int 1 36 | -- # :version int 37 | INSERT INTO simplepets_info (id, db_version) 38 | VALUES (:id, :version) 39 | ON DUPLICATE KEY UPDATE db_version = :version; 40 | -- # } 41 | -- # {registerpet 42 | -- # :petType string 43 | -- # :petName string 44 | -- # :petOwner string 45 | -- # :petSize float 46 | -- # :petBaby bool 47 | -- # :petVisible int 48 | -- # :extraData ?string 49 | -- # :enableInv bool 50 | -- # :enableRiding bool 51 | INSERT INTO simplepets_pets (petType, petName, petOwner, petSize, petBaby, petVisible, enableInv, enableRiding, 52 | extraData) 53 | VALUES (:petType, :petName, :petOwner, :petSize, :petBaby, :petVisible, :enableInv, :enableRiding, :extraData); 54 | -- # } 55 | -- # {savepet 56 | -- # :id int 57 | -- # :petType string 58 | -- # :petName string 59 | -- # :petOwner string 60 | -- # :petSize float 61 | -- # :petBaby bool 62 | -- # :petVisible int 63 | -- # :extraData ?string 64 | -- # :enableInv bool 65 | -- # :enableRiding bool 66 | UPDATE simplepets_pets 67 | SET petType = :petType, 68 | petName = :petName, 69 | petOwner = :petOwner, 70 | petSize = :petSize, 71 | petBaby = :petBaby, 72 | petVisible = :petVisible, 73 | enableInv = :enableInv, 74 | enableRiding = :enableRiding, 75 | extraData = :extraData 76 | WHERE id = :id; 77 | -- # } 78 | -- # {getpet 79 | -- # :petName string 80 | -- # :petOwner string 81 | SELECT * 82 | FROM simplepets_pets 83 | WHERE petName = :petName 84 | AND petOwner = :petOwner; 85 | -- # } 86 | -- # {getallpets 87 | -- # :petOwner string 88 | SELECT * 89 | FROM simplepets_pets 90 | WHERE petOwner = :petOwner; 91 | -- # } 92 | -- # {removepet 93 | -- # :id int 94 | DELETE 95 | FROM simplepets_pets 96 | WHERE id = :id; 97 | -- # } 98 | -- #} 99 | -------------------------------------------------------------------------------- /resources/sqlite.sql: -------------------------------------------------------------------------------- 1 | -- # !mysql 2 | -- # {simplepets 3 | -- # {init 4 | -- # {info 5 | CREATE TABLE IF NOT EXISTS simplepets_info 6 | ( 7 | id TINYINT UNSIGNED PRIMARY KEY, 8 | db_version TINYINT UNSIGNED NOT NULL DEFAULT 1 9 | ); 10 | -- # } 11 | -- # {data 12 | CREATE TABLE IF NOT EXISTS simplepets_pets 13 | ( 14 | id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 15 | petType VARCHAR(64) NOT NULL, 16 | petName VARCHAR(32) NOT NULL, 17 | petOwner VARCHAR(32) NOT NULL, 18 | petSize FLOAT(2) NOT NULL, 19 | petBaby BOOL NOT NULL, 20 | petVisible INT NOT NULL, 21 | enableInv BOOL NOT NULL, 22 | enableRiding BOOL NOT NULL, 23 | extraData TEXT 24 | ) 25 | -- # } 26 | -- # } 27 | -- # {get-version 28 | -- # :id int 1 29 | SELECT db_version 30 | FROM simplepets_info 31 | WHERE id = :id; 32 | -- # } 33 | -- # {set-version 34 | -- # :id int 1 35 | -- # :version int 36 | INSERT INTO simplepets_info (id, db_version) 37 | VALUES (:id, :version) 38 | ON DUPLICATE KEY UPDATE db_version = :version; 39 | -- # } 40 | -- # {registerpet 41 | -- # :petType string 42 | -- # :petName string 43 | -- # :petOwner string 44 | -- # :petSize float 45 | -- # :petBaby bool 46 | -- # :petVisible int 47 | -- # :extraData ?string 48 | -- # :enableInv bool 49 | -- # :enableRiding bool 50 | INSERT INTO simplepets_pets (petType, petName, petOwner, petSize, petBaby, petVisible, enableInv, enableRiding, 51 | extraData) 52 | VALUES (:petType, :petName, :petOwner, :petSize, :petBaby, :petVisible, :enableInv, :enableRiding, :extraData); 53 | -- # } 54 | -- # {savepet 55 | -- # :id int 56 | -- # :petType string 57 | -- # :petName string 58 | -- # :petOwner string 59 | -- # :petSize float 60 | -- # :petBaby bool 61 | -- # :petVisible int 62 | -- # :extraData ?string 63 | -- # :enableInv bool 64 | -- # :enableRiding bool 65 | UPDATE simplepets_pets 66 | SET petType = :petType, 67 | petName = :petName, 68 | petOwner = :petOwner, 69 | petSize = :petSize, 70 | petBaby = :petBaby, 71 | petVisible = :petVisible, 72 | enableInv = :enableInv, 73 | enableRiding = :enableRiding, 74 | extraData = :extraData 75 | WHERE id = :id; 76 | -- # } 77 | -- # {getpet 78 | -- # :petName string 79 | -- # :petOwner string 80 | SELECT * 81 | FROM simplepets_pets 82 | WHERE petName = :petName 83 | AND petOwner = :petOwner; 84 | -- # } 85 | -- # {getallpets 86 | -- # :petOwner string 87 | SELECT * 88 | FROM simplepets_pets 89 | WHERE petOwner = :petOwner; 90 | -- # } 91 | -- # {removepet 92 | -- # :id int 93 | DELETE 94 | FROM simplepets_pets 95 | WHERE id = :id; 96 | -- # } 97 | -- #} 98 | -------------------------------------------------------------------------------- /src/brokiem/simplepets/EventListener.php: -------------------------------------------------------------------------------- 1 | getPlayer(); 32 | 33 | SimplePets::getInstance()->addPlayer($player); 34 | Database::getInstance()->respawnPet($player); 35 | } 36 | 37 | public function onQuit(PlayerQuitEvent $event): void { 38 | $player = $event->getPlayer(); 39 | 40 | if (isset(PetManager::getInstance()->getActivePets()[$player->getName()])) { 41 | foreach (PetManager::getInstance()->getActivePets()[$player->getName()] as $petName => $petId) { 42 | $pet = $player->getServer()->getWorldManager()->findEntity($petId); 43 | 44 | if ($pet instanceof BasePet || $pet instanceof CustomPet) { 45 | $pet->despawn(); 46 | PetManager::getInstance()->removeActivePet($player, (string)$petName); 47 | } 48 | } 49 | } 50 | 51 | SimplePets::getInstance()->removePlayer($player); 52 | } 53 | 54 | public function onDamage(EntityDamageEvent $event): void { 55 | $entity = $event->getEntity(); 56 | 57 | if ($entity instanceof BasePet || $entity instanceof CustomPet) { 58 | $event->cancel(); 59 | } 60 | } 61 | 62 | public function onDataPacket(DataPacketReceiveEvent $event): void { 63 | $packet = $event->getPacket(); 64 | $player = $event->getOrigin()->getPlayer(); 65 | 66 | if ($player === null) { 67 | return; 68 | } 69 | 70 | if ($packet instanceof PlayerAuthInputPacket) { 71 | if ($packet->getMoveVecX() !== 0.0 or $packet->getMoveVecZ() !== 0.0) { 72 | $pet = PetManager::getInstance()->getRiddenPet($player); 73 | $pet?->walk($packet->getMoveVecX(), $packet->getMoveVecZ(), $player); 74 | } 75 | } elseif ($packet instanceof PlayerInputPacket) { 76 | if ($packet->motionX !== 0.0 or $packet->motionY !== 0.0) { 77 | $pet = PetManager::getInstance()->getRiddenPet($player); 78 | $pet?->walk($packet->motionX, $packet->motionY, $player); 79 | } 80 | } elseif ($packet instanceof InteractPacket) { 81 | if ($packet->action === InteractPacket::ACTION_LEAVE_VEHICLE) { 82 | $entity = $player->getServer()->getWorldManager()->findEntity($packet->targetActorRuntimeId); 83 | 84 | if ($entity instanceof BasePet || $entity instanceof CustomPet) { 85 | if ($entity->getRider()->getXuid() === $player->getXuid()) { 86 | $entity->unlink(); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | public function onDeath(PlayerDeathEvent $event): void { 94 | $player = $event->getPlayer(); 95 | 96 | PetManager::getInstance()->removeRiddenPet($player); 97 | } 98 | 99 | public function onTeleport(EntityTeleportEvent $event): void { 100 | $entity = $event->getEntity(); 101 | 102 | if ($entity instanceof Player) { 103 | PetManager::getInstance()->removeRiddenPet($entity); 104 | 105 | if (isset(PetManager::getInstance()->getActivePets()[$entity->getName()])) { 106 | foreach (PetManager::getInstance()->getActivePets()[$entity->getName()] as $petName => $petId) { 107 | $pet = $entity->getServer()->getWorldManager()->findEntity($petId); 108 | 109 | if ($pet instanceof BasePet || $pet instanceof CustomPet) { 110 | $location = clone $entity->getLocation(); 111 | ++$location->y; 112 | 113 | $pet->teleport($location); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /src/brokiem/simplepets/SimplePets.php: -------------------------------------------------------------------------------- 1 | getLogger()->debug("Checking virions"); 34 | $missing = $this->checkVirion(); 35 | if (!empty($missing)) { 36 | foreach ($missing as $class => $name) { 37 | $this->getLogger()->alert("Virion $class not found. ($name)"); 38 | } 39 | 40 | $this->getLogger()->alert("Please install the virion or download the plugin from poggit! Disabling plugin..."); 41 | $this->getServer()->getPluginManager()->disablePlugin($this); 42 | return; 43 | } 44 | 45 | self::setInstance($this); 46 | 47 | $this->getLogger()->debug("Loading pets"); 48 | $this->initPets(); 49 | 50 | $this->getLogger()->debug("Loading database"); 51 | $this->initDatabase(); 52 | 53 | $this->getLogger()->debug("Registering listener"); 54 | $this->getServer()->getPluginManager()->registerEvents(new EventListener(), $this); 55 | 56 | $this->getLogger()->debug("Registering command"); 57 | $this->getServer()->getCommandMap()->register("spet", new Command("spet", "SimplePet commands")); 58 | 59 | $this->getLogger()->debug("Plugin successfully enabled"); 60 | } 61 | 62 | private function checkVirion(): array { 63 | $virions = [ 64 | libasynql::class => "libasynql", 65 | InvMenu::class => "InvMenu" 66 | ]; 67 | $missing = []; 68 | 69 | foreach ($virions as $class => $name) { 70 | if (!class_exists($class)) { 71 | $missing[$class] = $name; 72 | } 73 | } 74 | 75 | return $missing; 76 | } 77 | 78 | private function initDatabase(): void { 79 | $this->database = libasynql::create($this, $this->getConfig()->get("database"), [ 80 | "sqlite" => "sqlite.sql", 81 | "mysql" => "mysql.sql" 82 | ]); 83 | 84 | $this->database->executeGeneric(Database::SIMPLEPETS_INIT_INFO); 85 | $this->database->executeGeneric(Database::SIMPLEPETS_INIT_DATA); 86 | 87 | $this->database->waitAll(); 88 | 89 | new Database(); 90 | } 91 | 92 | private function initPets(): void { 93 | if (!is_dir($this->getDataFolder() . "pets_inventory")) { 94 | mkdir($this->getDataFolder() . "pets_inventory"); 95 | } 96 | 97 | if (!InvMenuHandler::isRegistered()) { 98 | InvMenuHandler::register($this); 99 | } 100 | 101 | new PetManager(); 102 | } 103 | 104 | public function getDatabase(): DataConnector { 105 | return $this->database; 106 | } 107 | 108 | public function addPlayer(Player $player): void { 109 | if (!isset($this->players[$player->getXuid()])) { 110 | $this->players[$player->getXuid()] = $player->getName(); 111 | } 112 | } 113 | 114 | public function getPlayerByXuid(string $xuid): ?Player { 115 | if (isset($this->players[$xuid])) { 116 | return $this->getServer()->getPlayerExact($this->players[$xuid]); 117 | } 118 | 119 | return null; 120 | } 121 | 122 | public function removePlayer(Player $player): void { 123 | if (isset($this->players[$player->getXuid()])) { 124 | unset($this->players[$player->getXuid()]); 125 | } 126 | } 127 | 128 | protected function onDisable(): void { 129 | foreach ($this->getServer()->getWorldManager()->getWorlds() as $world) { 130 | foreach ($world->getEntities() as $entity) { 131 | if ($entity instanceof BasePet || $entity instanceof CustomPet) { 132 | $entity->saveNBT(); 133 | } 134 | } 135 | } 136 | 137 | if ($this->database instanceof DataConnector) { 138 | $this->database->waitAll(); 139 | $this->database->close(); 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /src/brokiem/simplepets/command/Command.php: -------------------------------------------------------------------------------- 1 | testPermission($sender)) { 35 | return; 36 | } 37 | 38 | if (isset($args[0])) { 39 | switch (strtolower($args[0])) { 40 | case "ui": 41 | if (!($sender instanceof Player)) { 42 | return; 43 | } 44 | 45 | if (!$sender->hasPermission("simplepets.ui")) { 46 | $sender->sendMessage("§cYou don't have permission to run this command"); 47 | return; 48 | } 49 | 50 | $form = new SimpleForm("Spawn Pet"); 51 | 52 | foreach (PetManager::getInstance()->getRegisteredPets() as $type => $class) { 53 | $form->addButton(new Button($type, null, function(Player $player) use ($type) { 54 | $dropdown = new Dropdown("Pet type"); 55 | $dropdown->addOption(new Option($type, $type)); 56 | 57 | $form = new CustomForm("Spawn $type"); 58 | $form->addElement("pet_type", $dropdown); 59 | $form->addElement("pet_name", new Input("Pet name")); 60 | 61 | $form->setSubmitListener(function(Player $player, FormResponse $response) { 62 | $pet_name = $response->getInputSubmittedText("pet_name") ?? $player->getName(); 63 | $pet_type = $response->getDropdownSubmittedOptionId("pet_type"); 64 | 65 | if ($pet_type === null) { 66 | return; 67 | } 68 | 69 | if (!Player::isValidUserName($pet_name)) { 70 | $player->sendMessage("Invalid pet name. Only supported [A-z] with max 16 length character;"); 71 | return; 72 | } 73 | 74 | if (isset(PetManager::getInstance()->getActivePets()[$player->getName()])) { 75 | foreach (PetManager::getInstance()->getActivePets()[$player->getName()] as $petName => $petId) { 76 | $pet = $player->getServer()->getWorldManager()->findEntity($petId); 77 | 78 | if ($pet instanceof BasePet || $pet instanceof CustomPet) { 79 | $pet->despawn(); 80 | } 81 | 82 | PetManager::getInstance()->removeActivePet($player, (string)$petName); 83 | Database::getInstance()->removePet($player, (string)$petName); 84 | } 85 | } 86 | 87 | PetManager::getInstance()->spawnPet($player, (string)$pet_type, (string)$pet_name); 88 | 89 | $player->sendMessage("§b" . str_replace("Pet", " Pet", $pet_type) . " §awith the name §b" . $pet_name . " §ahas been successfully spawned"); 90 | }); 91 | 92 | $player->sendForm($form); 93 | })); 94 | } 95 | 96 | $sender->sendForm($form); 97 | break; 98 | case "spawn": 99 | if (!$sender->hasPermission("simplepets.spawn")) { 100 | $sender->sendMessage("§cYou don't have permission to run this command"); 101 | return; 102 | } 103 | 104 | if (isset($args[1], $args[2])) { 105 | $player = Server::getInstance()->getPlayerByPrefix($args[1]); 106 | 107 | if ($player === null) { 108 | $sender->sendMessage("§cPlayer with name $args[1] doesnt exists"); 109 | return; 110 | } 111 | 112 | if (isset(PetManager::getInstance()->getActivePets()[$player->getName()][$args[2]])) { 113 | $sender->sendMessage("§c{$player->getName()} already have a pet with the name " . $args[2]); 114 | } else { 115 | if (isset(PetManager::getInstance()->getRegisteredPets()[$args[2]])) { 116 | if (isset($args[4]) && is_numeric($args[4]) and (float)$args[4] > 0 and (float)$args[4] < 10) { 117 | if (isset($args[5]) && is_bool($args[5])) { 118 | PetManager::getInstance()->spawnPet($player, $args[2], $args[3], (float)$args[4], (bool)$args[5]); 119 | } else { 120 | PetManager::getInstance()->spawnPet($player, $args[2], $args[3], (float)$args[4]); 121 | } 122 | } else { 123 | PetManager::getInstance()->spawnPet($player, $args[2], $args[3]); 124 | } 125 | 126 | $sender->sendMessage("§b" . str_replace("Pet", " Pet", $args[2]) . " §awith the name §b" . $args[3] . " §ahas been successfully spawned to §b" . $player->getName()); 127 | } else { 128 | $sender->sendMessage("§cPet with type §4" . $args[2] . " §cis not registered. §aTry §b/spet petlist §ato view registered pets"); 129 | } 130 | } 131 | } else { 132 | $sender->sendMessage("§cUsage: /spet spawn "); 133 | } 134 | break; 135 | case "remove": 136 | case "delete": 137 | if (!$sender->hasPermission("simplepets.remove")) { 138 | $sender->sendMessage("§cYou don't have permission to run this command"); 139 | return; 140 | } 141 | 142 | if (isset($args[1], $args[2])) { 143 | $player = Server::getInstance()->getPlayerByPrefix($args[1]); 144 | 145 | if ($player === null) { 146 | $sender->sendMessage("§cPlayer with name $args[1] doesnt exists"); 147 | return; 148 | } 149 | 150 | if (isset(PetManager::getInstance()->getActivePets()[$player->getName()][$args[2]])) { 151 | $id = PetManager::getInstance()->getActivePets()[$player->getName()][$args[2]]; 152 | $pet = Server::getInstance()->getWorldManager()->findEntity($id); 153 | 154 | if ($pet instanceof BasePet || $pet instanceof CustomPet) { 155 | $pet->despawn(); 156 | } 157 | 158 | PetManager::getInstance()->removeActivePet($player, (string)$args[2]); 159 | Database::getInstance()->removePet($player, $args[2]); 160 | $sender->sendMessage("§aPet with the name §b" . $args[2] . " §afrom §b{$player->getName()} §ahas been successfully removed"); 161 | } else { 162 | $sender->sendMessage("§a{$player->getName()} don't have a pet with the name §b" . $args[2]); 163 | } 164 | } else { 165 | $sender->sendMessage("§cUsage: /spet remove "); 166 | } 167 | break; 168 | case "inventory": 169 | case "inv": 170 | if (!SimplePets::getInstance()->getConfig()->get("enable-inventory")) { 171 | $sender->sendMessage("§cPet inventory feature is disabled!"); 172 | return; 173 | } 174 | 175 | if ($sender instanceof Player) { 176 | if (isset($args[2])) { 177 | if (!$sender->hasPermission("simplepets.inv.other")) { 178 | $sender->sendMessage("§cYou don't have permission to run this command"); 179 | return; 180 | } 181 | 182 | $player = Server::getInstance()->getPlayerByPrefix($args[1]); 183 | 184 | if ($player === null) { 185 | $sender->sendMessage("§cPlayer with name $args[1] doesnt exists"); 186 | } else { 187 | if (isset(PetManager::getInstance()->getActivePets()[$player->getName()][$args[2]])) { 188 | $id = PetManager::getInstance()->getActivePets()[$player->getName()][$args[2]]; 189 | $pet = Server::getInstance()->getWorldManager()->findEntity($id); 190 | 191 | if ($pet instanceof BasePet || $pet instanceof CustomPet) { 192 | $pet->getInventoryMenu()->send($player, $pet->getName()); 193 | } 194 | } else { 195 | $sender->sendMessage("§a{$player->getName()} don't have a pet with the name §b" . $args[2]); 196 | } 197 | 198 | return; 199 | } 200 | } elseif (isset($args[1])) { 201 | if (!$sender->hasPermission("simplepets.inv")) { 202 | $sender->sendMessage("§cYou don't have permission to run this command"); 203 | return; 204 | } 205 | 206 | if (isset(PetManager::getInstance()->getActivePets()[$sender->getName()][$args[1]])) { 207 | $id = PetManager::getInstance()->getActivePets()[$sender->getName()][$args[1]]; 208 | $pet = Server::getInstance()->getWorldManager()->findEntity($id); 209 | 210 | if ($pet instanceof BasePet || $pet instanceof CustomPet) { 211 | if ($pet->isInvEnabled()) { 212 | $pet->getInventoryMenu()->send($sender, $pet->getName()); 213 | } else { 214 | $sender->sendMessage("§cInventory access to your pet named §4" . $pet->getName() . " §cis disabled"); 215 | } 216 | } 217 | } else { 218 | $sender->sendMessage("§aYou don't have a pet with the name §b" . $args[1]); 219 | } 220 | } else { 221 | $sender->sendMessage("§cUsage: /spet inv "); 222 | } 223 | } 224 | break; 225 | case "ride": 226 | if (!SimplePets::getInstance()->getConfig()->get("enable-riding")) { 227 | $sender->sendMessage("§cPet riding feature is disabled!"); 228 | return; 229 | } 230 | 231 | if (!$sender->hasPermission("simplepets.ride")) { 232 | $sender->sendMessage("§cYou don't have permission to run this command"); 233 | return; 234 | } 235 | 236 | if ($sender instanceof Player) { 237 | if (isset($args[1])) { 238 | if (isset(PetManager::getInstance()->getActivePets()[$sender->getName()][$args[1]])) { 239 | $id = PetManager::getInstance()->getActivePets()[$sender->getName()][$args[1]]; 240 | $pet = Server::getInstance()->getWorldManager()->findEntity($id); 241 | 242 | if ($pet instanceof BasePet || $pet instanceof CustomPet) { 243 | if ($pet->isRidingEnabled()) { 244 | $pet->link($sender); 245 | } else { 246 | $sender->sendMessage("§cRiding access to your pet named §4" . $pet->getName() . " §cis disabled"); 247 | } 248 | } 249 | } else { 250 | $sender->sendMessage("§aYou don't have a pet with the name §b" . $args[1]); 251 | } 252 | } else { 253 | $sender->sendMessage("§cUsage: /spet ride "); 254 | } 255 | } 256 | break; 257 | case "petlist": 258 | if (!$sender->hasPermission("simplepets.petlist")) { 259 | $sender->sendMessage("§cYou don't have permission to run this command"); 260 | return; 261 | } 262 | 263 | $message = "§bSimplePets pet list:\n"; 264 | 265 | foreach (PetManager::getInstance()->getRegisteredPets() as $type => $class) { 266 | $message .= "§b- §a" . $type . "\n"; 267 | } 268 | 269 | $sender->sendMessage($message); 270 | break; 271 | case "help": 272 | $sender->sendMessage("\n§7---- ---- ---- - ---- ---- ----\n§eCommand List:\n§2» /spet petlist\n§2» /spet spawn \n§2» /spet remove \n§2» /spet inv \n§2» /spet ride \n§7---- ---- ---- - ---- ---- ----"); 273 | break; 274 | default: 275 | $sender->sendMessage(TextFormat::RED . "Subcommand '$args[0]' not found! Try '/spet help' for help."); 276 | } 277 | } else { 278 | $sender->sendMessage("§7---- ---- [ §aSimplePets§7 ] ---- ----\n§bAuthor: @brokiem\n§3Source Code: github.com/brokiem/SimplePets\nVersion " . $this->getOwningPlugin()->getDescription()->getVersion() . "\n§7---- ---- ---- - ---- ---- ----"); 279 | } 280 | } 281 | 282 | public function getOwningPlugin(): Plugin { 283 | return SimplePets::getInstance(); 284 | } 285 | } -------------------------------------------------------------------------------- /src/brokiem/simplepets/database/Database.php: -------------------------------------------------------------------------------- 1 | getDatabase(); 38 | $db->executeInsert(self::SIMPLEPETS_REGISTERPET, [ 39 | "petType" => $pet->getPetType(), 40 | "petName" => $pet->getPetName(), 41 | "petOwner" => $pet->getPetOwner(), 42 | "petSize" => $pet->getPetSize(), 43 | "petBaby" => $pet->isBabyPet(), 44 | "petVisible" => $pet->getPetVisibility(), 45 | "enableInv" => $pet->isInvEnabled(), 46 | "enableRiding" => $pet->isRidingEnabled(), 47 | "extraData" => null 48 | ]); 49 | } 50 | 51 | public function savePet(BasePet|CustomPet $pet): void { 52 | $db = SimplePets::getInstance()->getDatabase(); 53 | $db->executeSelect(self::SIMPLEPETS_GETPET, [ 54 | "petName" => $pet->getPetName(), 55 | "petOwner" => $pet->getPetOwner() 56 | ], function(array $rows) use ($pet, $db) { 57 | foreach ($rows as $row) { 58 | $db->executeInsert(self::SIMPLEPETS_SAVEPET, [ 59 | "id" => $row["id"], 60 | "petType" => $pet->getPetType(), 61 | "petName" => $pet->getPetName(), 62 | "petOwner" => $pet->getPetOwner(), 63 | "petSize" => $pet->getPetSize(), 64 | "petBaby" => $pet->isBabyPet(), 65 | "petVisible" => $pet->getPetVisibility(), 66 | "enableInv" => $pet->isInvEnabled(), 67 | "enableRiding" => $pet->isRidingEnabled(), 68 | "extraData" => null 69 | ]); 70 | } 71 | }); 72 | } 73 | 74 | public function removePet(Player $owner, string $petName): void { 75 | $db = SimplePets::getInstance()->getDatabase(); 76 | $db->executeSelect(self::SIMPLEPETS_GETPET, [ 77 | "petName" => $petName, 78 | "petOwner" => $owner->getXuid() 79 | ], function(array $rows) use ($db) { 80 | foreach ($rows as $row) { 81 | $db->executeGeneric(self::SIMPLEPETS_REMOVEPET, ["id" => $row["id"]]); 82 | } 83 | }); 84 | 85 | $file = SimplePets::getInstance()->getDataFolder() . "pets_inventory/" . $owner->getXuid() . "-" . $petName . ".dat"; 86 | if (is_file($file)) { 87 | unlink($file); 88 | } 89 | } 90 | 91 | public function respawnPet(Player $owner): void { 92 | SimplePets::getInstance()->getDatabase()->executeSelect(self::SIMPLEPETS_GETALLPETS, [ 93 | "petOwner" => $owner->getXuid() 94 | ], function(array $rows) use ($owner) { 95 | foreach ($rows as $row) { 96 | $type = $row["petType"]; 97 | $name = $row["petName"]; 98 | $size = $row["petSize"]; 99 | $baby = $row["petBaby"]; 100 | $visibility = $row["petVisible"]; 101 | $enableInv = $row["enableInv"]; 102 | $enableRiding = $row["enableRiding"]; 103 | $extraData = $row["extraData"]; 104 | 105 | PetManager::getInstance()->respawnPet($owner, $type, $name, $size, (bool)$baby, $visibility, (bool)$enableInv, (bool)$enableRiding, $extraData); 106 | } 107 | }); 108 | } 109 | } -------------------------------------------------------------------------------- /src/brokiem/simplepets/manager/PetManager.php: -------------------------------------------------------------------------------- 1 | AllayPet::class, 129 | "AxolotlPet" => AxolotlPet::class, 130 | "WardenPet" => WardenPet::class, 131 | "GoatPet" => GoatPet::class, 132 | "ArmorstandPet" => ArmorstandPet::class, 133 | "ArrowPet" => ArrowPet::class, 134 | "BatPet" => BatPet::class, 135 | "BeePet" => BeePet::class, 136 | "BlazePet" => BlazePet::class, 137 | "BoatPet" => BoatPet::class, 138 | "CatPet" => CatPet::class, 139 | "CavespiderPet" => CavespiderPet::class, 140 | "ChestminecartPet" => ChestminecartPet::class, 141 | "ChickenPet" => ChickenPet::class, 142 | "CodPet" => CodPet::class, 143 | "CommandblockminecartPet" => CommandblockminecartPet::class, 144 | "CowPet" => CowPet::class, 145 | "CreeperPet" => CreeperPet::class, 146 | "DolphinPet" => DolphinPet::class, 147 | "DonkeyPet" => DonkeyPet::class, 148 | "DragonfireballPet" => DragonfireballPet::class, 149 | "DrownedPet" => DrownedPet::class, 150 | "EggPet" => EggPet::class, 151 | "ElderguardianPet" => ElderguardianPet::class, 152 | "ElderguardianghostPet" => ElderguardianghostPet::class, 153 | "EndercrystalPet" => EndercrystalPet::class, 154 | "EnderdragonPet" => EnderdragonPet::class, 155 | "EndermanPet" => EndermanPet::class, 156 | "EndermitePet" => EndermitePet::class, 157 | "EnderpearlPet" => EnderpearlPet::class, 158 | "EvocationfangPet" => EvocationfangPet::class, 159 | "EvocationillagerPet" => EvocationillagerPet::class, 160 | "EyeofendersignalPet" => EyeofendersignalPet::class, 161 | "FireballPet" => FireballPet::class, 162 | "FoxPet" => FoxPet::class, 163 | "GhastPet" => GhastPet::class, 164 | "GuardianPet" => GuardianPet::class, 165 | "HoglinPet" => HoglinPet::class, 166 | "HopperminecartPet" => HopperminecartPet::class, 167 | "HorsePet" => HorsePet::class, 168 | "HuskPet" => HuskPet::class, 169 | "IrongolemPet" => IrongolemPet::class, 170 | "LingeringpotionPet" => LingeringpotionPet::class, 171 | "LlamaPet" => LlamaPet::class, 172 | "MagmacubePet" => MagmacubePet::class, 173 | "MinecartPet" => MinecartPet::class, 174 | "MooshroomPet" => MooshroomPet::class, 175 | "MulePet" => MulePet::class, 176 | "OcelotPet" => OcelotPet::class, 177 | "PandaPet" => PandaPet::class, 178 | "ParrotPet" => ParrotPet::class, 179 | "PhantomPet" => PhantomPet::class, 180 | "PigPet" => PigPet::class, 181 | "PiglinPet" => PiglinPet::class, 182 | "PillagerPet" => PillagerPet::class, 183 | "PolarbearPet" => PolarbearPet::class, 184 | "PufferfishPet" => PufferfishPet::class, 185 | "RabbitPet" => RabbitPet::class, 186 | "RavagerPet" => RavagerPet::class, 187 | "SalmonPet" => SalmonPet::class, 188 | "SheepPet" => SheepPet::class, 189 | "ShulkerPet" => ShulkerPet::class, 190 | "ShulkerbulletPet" => ShulkerbulletPet::class, 191 | "SilverfishPet" => SilverfishPet::class, 192 | "SkeletonPet" => SkeletonPet::class, 193 | "SkeletonhorsePet" => SkeletonhorsePet::class, 194 | "SlimePet" => SlimePet::class, 195 | "SmallfireballPet" => SmallfireballPet::class, 196 | "SnowballPet" => SnowballPet::class, 197 | "SnowgolemPet" => SnowgolemPet::class, 198 | "SpiderPet" => SpiderPet::class, 199 | "SplashpotionPet" => SplashpotionPet::class, 200 | "SquidPet" => SquidPet::class, 201 | "StrayPet" => StrayPet::class, 202 | "StriderPet" => StriderPet::class, 203 | "TntPet" => TntPet::class, 204 | "TntminecartPet" => TntminecartPet::class, 205 | "TropicalfishPet" => TropicalfishPet::class, 206 | "TurtlePet" => TurtlePet::class, 207 | "VexPet" => VexPet::class, 208 | "VillagerPet" => VillagerPet::class, 209 | "Villagerv2Pet" => Villagerv2Pet::class, 210 | "VindicatorPet" => VindicatorPet::class, 211 | "WanderingtraderPet" => WanderingtraderPet::class, 212 | "WitchPet" => WitchPet::class, 213 | "WitherPet" => WitherPet::class, 214 | "WitherskeletonPet" => WitherskeletonPet::class, 215 | "WitherskullPet" => WitherskullPet::class, 216 | "WolfPet" => WolfPet::class, 217 | "ZoglinPet" => ZoglinPet::class, 218 | "ZombiePet" => ZombiePet::class, 219 | "ZombiehorsePet" => ZombiehorsePet::class, 220 | "ZombiepigmanPet" => ZombiepigmanPet::class, 221 | "ZombievillagerPet" => ZombievillagerPet::class, 222 | "Zombievillagerv2Pet" => Zombievillagerv2Pet::class 223 | ]; 224 | 225 | private array $registered_pets = []; 226 | private array $active_pets = []; 227 | private array $ridden_pet = []; 228 | 229 | public const VISIBLE_TO_EVERYONE = 0; 230 | public const VISIBLE_TO_OWNER = 1; 231 | public const INVISIBLE_TO_EVERYONE = 3; 232 | 233 | public function __construct() { 234 | self::setInstance($this); 235 | foreach ($this->default_pets as $type => $class) { 236 | self::registerEntity($class, [$type]); 237 | $this->registerPet($type, $class); 238 | } 239 | } 240 | 241 | public static function registerEntity(string $entityClass, array $saveNames = []): void { 242 | if (!class_exists($entityClass)) { 243 | throw new \RuntimeException("Class $entityClass not found."); 244 | } 245 | 246 | $refClass = new \ReflectionClass($entityClass); 247 | if (is_a($entityClass, BasePet::class, true) || is_a($entityClass, CustomPet::class, true) and !$refClass->isAbstract()) { 248 | if (is_a($entityClass, CustomPet::class, true)) { 249 | EntityFactory::getInstance()->register($entityClass, function(World $world, CompoundTag $nbt) use ($entityClass): Entity { 250 | return new $entityClass(EntityDataHelper::parseLocation($nbt, $world), Human::parseSkinNBT($nbt), $nbt); 251 | }, array_merge([$entityClass], $saveNames)); 252 | } else { 253 | EntityFactory::getInstance()->register($entityClass, function(World $world, CompoundTag $nbt) use ($entityClass): Entity { 254 | return new $entityClass(EntityDataHelper::parseLocation($nbt, $world), $nbt); 255 | }, array_merge([$entityClass], $saveNames)); 256 | } 257 | } 258 | } 259 | 260 | public function registerPet(string $type, string $class): void { 261 | if (!class_exists($class)) { 262 | throw new \RuntimeException("Class $class not found."); 263 | } 264 | 265 | $refClass = new \ReflectionClass($class); 266 | if (is_a($class, BasePet::class, true) || is_a($class, CustomPet::class, true) and !$refClass->isAbstract()) { 267 | $this->registered_pets[$type] = $class; 268 | } 269 | } 270 | 271 | public function spawnPet(Player $owner, string $petType, string $petName, float $petSize = 1, bool $petBaby = false, int $petVis = PetManager::VISIBLE_TO_EVERYONE, bool $enableInv = true, bool $enableRiding = true, ?string $extraData = "null"): void { 272 | $location = clone $owner->getLocation(); 273 | ++$location->y; 274 | 275 | $nbt = $this->createBaseNBT($location); 276 | $nbt->setString("petOwner", $owner->getXuid()) 277 | ->setString("petName", $petName) 278 | ->setFloat("petSize", $petSize) 279 | ->setInt("petBaby", (int)$petBaby) 280 | ->setInt("petVisibility", $petVis) 281 | ->setInt("invEnabled", (int)$enableInv) 282 | ->setInt("ridingEnabled", (int)$enableRiding) 283 | ->setString("extraData", $extraData ?? "null"); 284 | $pet = $this->createEntity($petType, $location, $nbt); 285 | 286 | if ($pet !== null) { 287 | $pet->setPetName($petName); 288 | $pet->setPetBaby($petBaby); 289 | $pet->setPetVisibility($petVis); 290 | $pet->spawnToAll(); 291 | 292 | $this->active_pets[$owner->getName()][$pet->getPetName()] = $pet->getId(); 293 | Database::getInstance()->registerPet($pet); 294 | } 295 | } 296 | 297 | public function respawnPet(Player $owner, string $petType, string $petName, float $petSize = 1, bool $petBaby = false, int $petVis = PetManager::VISIBLE_TO_EVERYONE, bool $enableInv = true, bool $enableRiding = true, ?string $extraData = "null"): void { 298 | $location = clone $owner->getLocation(); 299 | ++$location->y; 300 | 301 | $nbt = $this->createBaseNBT($location); 302 | $nbt->setString("petOwner", $owner->getXuid()) 303 | ->setString("petName", $petName) 304 | ->setFloat("petSize", $petSize) 305 | ->setInt("petBaby", (int)$petBaby) 306 | ->setInt("petVisibility", $petVis) 307 | ->setInt("invEnabled", (int)$enableInv) 308 | ->setInt("ridingEnabled", (int)$enableRiding) 309 | ->setString("extraData", $extraData ?? "null"); 310 | $pet = $this->createEntity($petType, $location, $nbt); 311 | 312 | if ($pet !== null) { 313 | $pet->setPetName($petName); 314 | $pet->setPetBaby($petBaby); 315 | $pet->setPetVisibility($petVis); 316 | $pet->spawnToAll(); 317 | 318 | $this->active_pets[$owner->getName()][$pet->getPetName()] = $pet->getId(); 319 | } 320 | } 321 | 322 | public function addRiddenPet(Player $owner, BasePet|CustomPet $pet): void { 323 | $this->ridden_pet[$owner->getName()] = $pet; 324 | } 325 | 326 | public function getRegisteredPets(): array { 327 | return $this->registered_pets; 328 | } 329 | 330 | public function getActivePets(): array { 331 | return $this->active_pets; 332 | } 333 | 334 | public function getRiddenPet(Player $owner): null|BasePet|CustomPet { 335 | return $this->ridden_pet[$owner->getName()] ?? null; 336 | } 337 | 338 | public function removeRiddenPet(Player $owner): void { 339 | unset($this->ridden_pet[$owner->getName()]); 340 | } 341 | 342 | public function removeActivePet(Player $owner, string $petName): bool { 343 | $this->removeRiddenPet($owner); 344 | 345 | if (isset($this->active_pets[$owner->getName()][$petName])) { 346 | unset($this->active_pets[$owner->getName()][$petName]); 347 | return true; 348 | } 349 | 350 | return false; 351 | } 352 | 353 | /** 354 | * Helper function which creates minimal NBT needed to spawn an entity. 355 | */ 356 | public function createBaseNBT(Vector3 $pos, ?Vector3 $motion = null, float $yaw = 0.0, float $pitch = 0.0): CompoundTag { 357 | return CompoundTag::create() 358 | ->setTag("Pos", new ListTag([ 359 | new DoubleTag($pos->x), 360 | new DoubleTag($pos->y), 361 | new DoubleTag($pos->z) 362 | ])) 363 | ->setTag("Motion", new ListTag([ 364 | new DoubleTag($motion !== null ? $motion->x : 0.0), 365 | new DoubleTag($motion !== null ? $motion->y : 0.0), 366 | new DoubleTag($motion !== null ? $motion->z : 0.0) 367 | ])) 368 | ->setTag("Rotation", new ListTag([ 369 | new FloatTag($yaw), 370 | new FloatTag($pitch) 371 | ])); 372 | } 373 | 374 | public function createEntity(string $type, Location $location, CompoundTag $nbt): null|BasePet|CustomPet { 375 | if (isset($this->registered_pets[$type])) { 376 | /** @var BasePet|CustomPet $class */ 377 | $class = $this->registered_pets[$type]; 378 | 379 | if (is_a($class, BasePet::class, true)) { 380 | return new $class($location, $nbt); 381 | } 382 | 383 | if (is_a($class, CustomPet::class, true)) { 384 | return new $class($location, Human::parseSkinNBT($nbt), $nbt); 385 | } 386 | } 387 | 388 | return null; 389 | } 390 | } -------------------------------------------------------------------------------- /src/brokiem/simplepets/pets/AllayPet.php: -------------------------------------------------------------------------------- 1 | petOwner = $nbt->getString("petOwner"); 55 | $this->petName = $nbt->getString("petName"); 56 | $this->petSize = $nbt->getFloat("petSize", 1); 57 | $this->petBaby = (bool)$nbt->getInt("petBaby", 0); 58 | $this->petVisibility = $nbt->getInt("petVisibility", PetManager::VISIBLE_TO_EVERYONE); 59 | $this->invEnabled = (bool)$nbt->getInt("invEnabled", 1); 60 | $this->ridingEnabled = (bool)$nbt->getInt("ridingEnabled", 1); 61 | $this->extraData = $nbt->getString("extraData") === "" ? null : $nbt->getString("extraData"); 62 | } 63 | 64 | parent::__construct($location, $nbt); 65 | $this->setNameTagAlwaysVisible(); 66 | $this->setCanSaveWithChunk(false); 67 | 68 | $this->setMaxHealth(20); 69 | $this->setHealth(20); 70 | } 71 | 72 | abstract public static function getNetworkTypeId(): string; 73 | 74 | abstract public function getPetType(): string; 75 | 76 | public function getPetOwner(): ?string { 77 | return $this->petOwner; 78 | } 79 | 80 | public function setPetOwner(string $xuid): void { 81 | $this->petOwner = $xuid; 82 | } 83 | 84 | public function getPetName(): ?string { 85 | return $this->petName; 86 | } 87 | 88 | public function setPetName(string $name): void { 89 | $this->petName = $name; 90 | $this->setNameTag($name); 91 | } 92 | 93 | public function getPetSize(): float { 94 | return $this->petSize; 95 | } 96 | 97 | public function setPetSize(float $size): void { 98 | $this->petSize = $size; 99 | $this->setScale($size); 100 | } 101 | 102 | public function setPetBaby(bool $val): void { 103 | $this->petBaby = $val; 104 | 105 | $this->getNetworkProperties()->setGenericFlag(EntityMetadataFlags::BABY, $val); 106 | } 107 | 108 | public function isBabyPet(): bool { 109 | return $this->petBaby; 110 | } 111 | 112 | public function setPetVisibility(int $val): void { 113 | $this->petVisibility = $val; 114 | 115 | switch ($val) { 116 | case PetManager::VISIBLE_TO_EVERYONE: 117 | $this->despawnFromAll(); 118 | $this->spawnToAll(); 119 | break; 120 | case PetManager::VISIBLE_TO_OWNER: 121 | if ($this->getPetOwner() !== null) { 122 | $owner = SimplePets::getInstance()->getPlayerByXuid($this->getPetOwner()); 123 | 124 | if ($owner !== null) { 125 | $this->despawnFromAll(); 126 | $this->spawnTo($owner); 127 | } 128 | } 129 | break; 130 | case PetManager::INVISIBLE_TO_EVERYONE: 131 | $this->despawnFromAll(); 132 | break; 133 | } 134 | } 135 | 136 | public function getPetVisibility(): int { 137 | return $this->petVisibility; 138 | } 139 | 140 | public function setInvEnabled(bool $val): void { 141 | $this->invEnabled = $val; 142 | } 143 | 144 | public function isInvEnabled(): bool { 145 | return $this->invEnabled; 146 | } 147 | 148 | public function setRidingEnabled(bool $val): void { 149 | $this->ridingEnabled = $val; 150 | } 151 | 152 | public function isRidingEnabled(): bool { 153 | return $this->ridingEnabled; 154 | } 155 | 156 | public function getPetExtraData(): ?string { 157 | return $this->extraData; 158 | } 159 | 160 | public function link(Player $rider): void { 161 | $rider->getNetworkProperties()->setGenericFlag(EntityMetadataFlags::RIDING, true); 162 | $rider->getNetworkProperties()->setVector3(EntityMetadataProperties::RIDER_SEAT_POSITION, new Vector3(0, $this->getInitialSizeInfo()->getHeight() + 1, 0)); 163 | 164 | $pk = new SetActorLinkPacket(); 165 | $pk->link = new EntityLink($this->getId(), $rider->getId(), EntityLink::TYPE_RIDER, false, true); 166 | $rider->getServer()->broadcastPackets($this->getViewers(), [$pk]); 167 | 168 | PetManager::getInstance()->addRiddenPet($rider, $this); 169 | $this->rider = $rider->getXuid(); 170 | } 171 | 172 | public function unlink(): void { 173 | if ($this->getRider() !== null) { 174 | $this->getRider()->getNetworkProperties()->setGenericFlag(EntityMetadataFlags::RIDING, false); 175 | $this->getRider()->getNetworkProperties()->setVector3(EntityMetadataProperties::RIDER_SEAT_POSITION, new Vector3(0, 0, 0)); 176 | 177 | $pk = new SetActorLinkPacket(); 178 | $pk->link = new EntityLink($this->getId(), $this->getRider()->getId(), EntityLink::TYPE_REMOVE, false, true); 179 | $this->getRider()->getServer()->broadcastPackets($this->getViewers(), [$pk]); 180 | 181 | PetManager::getInstance()->removeRiddenPet($this->getRider()); 182 | } 183 | 184 | $this->rider = null; 185 | } 186 | 187 | public function getRider(): ?Player { 188 | return SimplePets::getInstance()->getPlayerByXuid($this->rider); 189 | } 190 | 191 | public function getName(): string { 192 | return $this->petName ?? "s_pet_no_name"; 193 | } 194 | 195 | public function getInventoryMenu(): InvMenu { 196 | return $this->petInventoryMenu; 197 | } 198 | 199 | public function despawn(): void { 200 | if (!$this->isFlaggedForDespawn()) { 201 | $this->flagForDespawn(); 202 | } 203 | } 204 | 205 | public function saveInventory(ListTag $petInventoryTag): void { 206 | $nbt = CompoundTag::create()->setTag("PetInventory", $petInventoryTag); 207 | $file = SimplePets::getInstance()->getDataFolder() . "pets_inventory/" . $this->getPetOwner() . "-" . $this->getName() . ".dat"; 208 | file_put_contents($file, zlib_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($nbt)), ZLIB_ENCODING_GZIP)); 209 | } 210 | 211 | public function getSavedInventory(): ?CompoundTag { 212 | $file = SimplePets::getInstance()->getDataFolder() . "pets_inventory/" . $this->getPetOwner() . "-" . $this->getName() . ".dat"; 213 | 214 | if (is_file($file)) { 215 | $decompressed = @zlib_decode(file_get_contents($file)); 216 | return (new LittleEndianNbtSerializer())->read($decompressed)->mustGetCompoundTag(); 217 | } 218 | 219 | return null; 220 | } 221 | 222 | // hehe thx blockpet 223 | public function walk(float $motionX, float $motionZ, Player $rider): void { 224 | $this->location->yaw = $rider->getLocation()->yaw; 225 | $this->location->pitch = $rider->getLocation()->pitch; 226 | $this->scheduleUpdate(); 227 | 228 | $direction_plane = $this->getDirectionPlane(); 229 | $x = $direction_plane->x / 2.5; 230 | $z = $direction_plane->y / 2.5; 231 | 232 | switch ($motionZ) { 233 | case 1: 234 | $finalMotionX = $x; 235 | $finalMotionZ = $z; 236 | break; 237 | case -1: 238 | $finalMotionX = -$x; 239 | $finalMotionZ = -$z; 240 | break; 241 | default: 242 | $average = $x + $z / 2; 243 | $finalMotionX = $average / 1.414 * $motionZ; 244 | $finalMotionZ = $average / 1.414 * $motionX; 245 | break; 246 | } 247 | 248 | switch ($motionX) { 249 | case 1: 250 | $finalMotionX = $z; 251 | $finalMotionZ = -$x; 252 | break; 253 | case -1: 254 | $finalMotionX = -$z; 255 | $finalMotionZ = $x; 256 | break; 257 | } 258 | 259 | if ($this->shouldJump()) { 260 | $this->jump(); 261 | } 262 | 263 | $this->move($finalMotionX, $this->motion->y, $finalMotionZ); 264 | $this->updateMovement(); 265 | } 266 | 267 | public function flagForDespawn(): void { 268 | $this->saveNBT(); 269 | 270 | parent::flagForDespawn(); 271 | } 272 | 273 | protected function initEntity(CompoundTag $nbt): void { 274 | parent::initEntity($nbt); 275 | 276 | $this->petInventoryMenu = InvMenu::create(InvMenuTypeIds::TYPE_CHEST); 277 | 278 | $petInventoryTag = $this->getSavedInventory(); 279 | if ($petInventoryTag !== null) { 280 | $inv = $petInventoryTag->getListTag("PetInventory"); 281 | if ($inv !== null) { 282 | /** @var CompoundTag $item */ 283 | foreach ($inv as $item) { 284 | $this->petInventoryMenu->getInventory()->setItem($item->getByte("Slot"), Item::nbtDeserialize($item)); 285 | } 286 | } 287 | } 288 | } 289 | 290 | public function saveNBT(): CompoundTag { 291 | $nbt = parent::saveNBT(); 292 | 293 | if ($this->petInventoryMenu !== null) { 294 | /** @var CompoundTag[] $items */ 295 | $items = []; 296 | 297 | $slotCount = $this->petInventoryMenu->getInventory()->getSize(); 298 | for ($slot = 0; $slot < $slotCount; ++$slot) { 299 | $item = $this->petInventoryMenu->getInventory()->getItem($slot); 300 | if (!$item->isNull()) { 301 | $items[] = $item->nbtSerialize($slot); 302 | } 303 | } 304 | 305 | $this->saveInventory(new ListTag($items, NBT::TAG_Compound)); 306 | } 307 | 308 | return $nbt; 309 | } 310 | 311 | protected function entityBaseTick(int $tickDiff = 1): bool { 312 | if ($this->rider !== null or $this->isClosed()) { 313 | return parent::entityBaseTick($tickDiff); 314 | } 315 | 316 | $this->followOwner(); 317 | 318 | if ($this->checkVal <= 0) { 319 | $this->checkVal = 60; 320 | 321 | $owner = $this->getPetOwner(); 322 | 323 | if ($owner !== null) { 324 | $target = SimplePets::getInstance()->getPlayerByXuid($owner); 325 | 326 | if (($target !== null) && $this->getPosition()->distance($target->getPosition()) >= 20) { 327 | $this->teleport($target->getPosition()); 328 | } 329 | } 330 | } 331 | 332 | --$this->checkVal; 333 | return parent::entityBaseTick($tickDiff); 334 | } 335 | 336 | public function followOwner(): void { 337 | $owner = $this->getPetOwner(); 338 | 339 | if ($owner === null) { 340 | return; 341 | } 342 | 343 | $target = SimplePets::getInstance()->getPlayerByXuid($owner); 344 | 345 | if ($target === null) { 346 | return; 347 | } 348 | 349 | if ($this->getPosition()->distance($target->getPosition()) <= 2) { 350 | return; 351 | } 352 | 353 | if ($this->shouldJump()) { 354 | $this->jump(); 355 | } 356 | 357 | $x = $target->getLocation()->x - $this->getLocation()->x; 358 | $y = $target->getLocation()->y - $this->getLocation()->y; 359 | $z = $target->getLocation()->z - $this->getLocation()->z; 360 | /** @noinspection RandomApiMigrationInspection */ 361 | if ($x * $x + $z * $z < mt_rand(3, 8)) { 362 | $this->motion->x = 0; 363 | $this->motion->z = 0; 364 | } else { 365 | $this->motion->x = 0.17 * ($x / (abs($x) + abs($z))); 366 | $this->motion->z = 0.17 * ($z / (abs($x) + abs($z))); 367 | } 368 | 369 | $this->getLocation()->yaw = rad2deg(atan2(-$x, $z)); 370 | $this->getLocation()->pitch = rad2deg(-atan2($y, sqrt($x * $x + $z * $z))); 371 | 372 | $this->move($this->motion->x, $this->motion->y, $this->motion->z); 373 | $this->lookAt($target->getPosition()); 374 | 375 | $this->updateMovement(); 376 | } 377 | 378 | public function shouldJump(): bool { 379 | if ($this->getBlockInFront()->getId() !== BlockLegacyIds::AIR) { 380 | return $this->getBlockInFront(1)->getId() === BlockLegacyIds::AIR; 381 | } 382 | 383 | if ($this->getBlockInFront(-0.9) instanceof Carpet) { 384 | return !($this->getWorld()->getBlock($this->getPosition()->add(0, -0.9, 0)) instanceof Carpet); 385 | } 386 | 387 | if ($this->getBlockInFront() instanceof Flowable) { 388 | return false; 389 | } 390 | 391 | if ($this->getBlockInFront() instanceof Liquid) { 392 | return true; 393 | } 394 | 395 | return false; 396 | } 397 | 398 | public function getBlockInFront(float $y = 0): Block { 399 | $pos = $this->getPosition()->add($this->getDirectionVector()->x * $this->getScale(), $y, $this->getDirectionVector()->z * $this->getScale())->round(); 400 | return $this->getWorld()->getBlock($pos); 401 | } 402 | } -------------------------------------------------------------------------------- /src/brokiem/simplepets/pets/base/CustomPet.php: -------------------------------------------------------------------------------- 1 | petOwner = $nbt->getString("petOwner"); 56 | $this->petName = $nbt->getString("petName"); 57 | $this->petSize = $nbt->getFloat("petSize", 1); 58 | $this->petBaby = (bool)$nbt->getInt("petBaby", 0); 59 | $this->petVisibility = $nbt->getInt("petVisibility", PetManager::VISIBLE_TO_EVERYONE); 60 | $this->invEnabled = (bool)$nbt->getInt("invEnabled", 1); 61 | $this->ridingEnabled = (bool)$nbt->getInt("ridingEnabled", 1); 62 | $this->extraData = $nbt->getString("extraData") === "" ? null : $nbt->getString("extraData"); 63 | } 64 | 65 | parent::__construct($location, $skin, $nbt); 66 | $this->setNameTagAlwaysVisible(); 67 | $this->setCanSaveWithChunk(false); 68 | 69 | $this->setMaxHealth(20); 70 | $this->setHealth(20); 71 | } 72 | 73 | abstract public function getPetType(): string; 74 | 75 | public function getPetOwner(): ?string { 76 | return $this->petOwner; 77 | } 78 | 79 | public function setPetOwner(string $xuid): void { 80 | $this->petOwner = $xuid; 81 | } 82 | 83 | public function getPetName(): ?string { 84 | return $this->petName; 85 | } 86 | 87 | public function setPetName(string $name): void { 88 | $this->petName = $name; 89 | $this->setNameTag($name); 90 | } 91 | 92 | public function getPetSize(): float { 93 | return $this->petSize; 94 | } 95 | 96 | public function setPetSize(float $size): void { 97 | $this->petSize = $size; 98 | $this->setScale($size); 99 | } 100 | 101 | public function setPetBaby(bool $val): void { 102 | $this->petBaby = $val; 103 | 104 | $this->getNetworkProperties()->setGenericFlag(EntityMetadataFlags::BABY, $val); 105 | } 106 | 107 | public function isBabyPet(): bool { 108 | return $this->petBaby; 109 | } 110 | 111 | public function setPetVisibility(int $val): void { 112 | $this->petVisibility = $val; 113 | 114 | switch ($val) { 115 | case PetManager::VISIBLE_TO_EVERYONE: 116 | $this->despawnFromAll(); 117 | $this->spawnToAll(); 118 | break; 119 | case PetManager::VISIBLE_TO_OWNER: 120 | if ($this->getPetOwner() !== null) { 121 | $owner = SimplePets::getInstance()->getPlayerByXuid($this->getPetOwner()); 122 | 123 | if ($owner !== null) { 124 | $this->despawnFromAll(); 125 | $this->spawnTo($owner); 126 | } 127 | } 128 | break; 129 | case PetManager::INVISIBLE_TO_EVERYONE: 130 | $this->despawnFromAll(); 131 | break; 132 | } 133 | } 134 | 135 | public function getPetVisibility(): int { 136 | return $this->petVisibility; 137 | } 138 | 139 | public function setInvEnabled(bool $val): void { 140 | $this->invEnabled = $val; 141 | } 142 | 143 | public function isInvEnabled(): bool { 144 | return $this->invEnabled; 145 | } 146 | 147 | public function setRidingEnabled(bool $val): void { 148 | $this->ridingEnabled = $val; 149 | } 150 | 151 | public function isRidingEnabled(): bool { 152 | return $this->ridingEnabled; 153 | } 154 | 155 | public function getPetExtraData(): ?string { 156 | return $this->extraData; 157 | } 158 | 159 | public function getInventoryMenu(): InvMenu { 160 | return $this->petInventoryMenu; 161 | } 162 | 163 | public function link(Player $rider): void { 164 | $rider->getNetworkProperties()->setGenericFlag(EntityMetadataFlags::RIDING, true); 165 | $rider->getNetworkProperties()->setVector3(EntityMetadataProperties::RIDER_SEAT_POSITION, new Vector3(0, $this->getInitialSizeInfo()->getHeight() + 1, 0)); 166 | 167 | $pk = new SetActorLinkPacket(); 168 | $pk->link = new EntityLink($this->getId(), $rider->getId(), EntityLink::TYPE_RIDER, false, true); 169 | $rider->getServer()->broadcastPackets($this->getViewers(), [$pk]); 170 | 171 | PetManager::getInstance()->addRiddenPet($rider, $this); 172 | $this->rider = $rider->getXuid(); 173 | } 174 | 175 | public function unlink(): void { 176 | if ($this->rider !== null) { 177 | if ($this->getRider() !== null) { 178 | $this->getRider()->getNetworkProperties()->setGenericFlag(EntityMetadataFlags::RIDING, false); 179 | $this->getRider()->getNetworkProperties()->setVector3(EntityMetadataProperties::RIDER_SEAT_POSITION, new Vector3(0, 0, 0)); 180 | 181 | $pk = new SetActorLinkPacket(); 182 | $pk->link = new EntityLink($this->getId(), $this->getRider()->getId(), EntityLink::TYPE_REMOVE, false, true); 183 | $this->getRider()->getServer()->broadcastPackets($this->getViewers(), [$pk]); 184 | 185 | PetManager::getInstance()->removeRiddenPet($this->getRider()); 186 | } 187 | 188 | $this->rider = null; 189 | } 190 | } 191 | 192 | public function getRider(): ?Player { 193 | return SimplePets::getInstance()->getPlayerByXuid($this->rider); 194 | } 195 | 196 | public function despawn(): void { 197 | if (!$this->isFlaggedForDespawn()) { 198 | $this->flagForDespawn(); 199 | } 200 | } 201 | 202 | public function saveInventory(ListTag $petInventoryTag): void { 203 | $nbt = CompoundTag::create()->setTag("PetInventory", $petInventoryTag); 204 | $file = SimplePets::getInstance()->getDataFolder() . "pets_inventory/" . $this->getPetOwner() . "-" . $this->getName() . ".dat"; 205 | file_put_contents($file, zlib_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($nbt)), ZLIB_ENCODING_GZIP)); 206 | } 207 | 208 | public function getSavedInventory(): ?CompoundTag { 209 | $file = SimplePets::getInstance()->getDataFolder() . "pets_inventory/" . $this->getPetOwner() . "-" . $this->getName() . ".dat"; 210 | 211 | if (is_file($file)) { 212 | $decompressed = @zlib_decode(file_get_contents($file)); 213 | return (new LittleEndianNbtSerializer())->read($decompressed)->mustGetCompoundTag(); 214 | } 215 | 216 | return null; 217 | } 218 | 219 | public function walk(float $motionX, float $motionZ, Player $rider): void { 220 | $this->location->yaw = $rider->getLocation()->yaw; 221 | $this->location->pitch = $rider->getLocation()->pitch; 222 | $this->scheduleUpdate(); 223 | 224 | $direction_plane = $this->getDirectionPlane(); 225 | $x = $direction_plane->x / 2.5; 226 | $z = $direction_plane->y / 2.5; 227 | 228 | switch ($motionZ) { 229 | case 1: 230 | $finalMotionX = $x; 231 | $finalMotionZ = $z; 232 | break; 233 | case -1: 234 | $finalMotionX = -$x; 235 | $finalMotionZ = -$z; 236 | break; 237 | default: 238 | $average = $x + $z / 2; 239 | $finalMotionX = $average / 1.414 * $motionZ; 240 | $finalMotionZ = $average / 1.414 * $motionX; 241 | break; 242 | } 243 | 244 | switch ($motionX) { 245 | case 1: 246 | $finalMotionX = $z; 247 | $finalMotionZ = -$x; 248 | break; 249 | case -1: 250 | $finalMotionX = -$z; 251 | $finalMotionZ = $x; 252 | break; 253 | } 254 | 255 | if ($this->shouldJump()) { 256 | $this->jump(); 257 | } 258 | 259 | $this->move($finalMotionX, $this->motion->y, $finalMotionZ); 260 | $this->updateMovement(); 261 | } 262 | 263 | protected function initEntity(CompoundTag $nbt): void { 264 | parent::initEntity($nbt); 265 | 266 | $this->petInventoryMenu = InvMenu::create(InvMenuTypeIds::TYPE_CHEST); 267 | 268 | $petInventoryTag = $this->getSavedInventory(); 269 | if ($petInventoryTag !== null) { 270 | $inv = $petInventoryTag->getListTag("PetInventory"); 271 | if ($inv !== null) { 272 | /** @var CompoundTag $item */ 273 | foreach ($inv as $item) { 274 | $this->petInventoryMenu->getInventory()->setItem($item->getByte("Slot"), Item::nbtDeserialize($item)); 275 | } 276 | } 277 | } 278 | } 279 | 280 | public function saveNBT(): CompoundTag { 281 | $nbt = parent::saveNBT(); 282 | 283 | if ($this->petInventoryMenu !== null) { 284 | /** @var CompoundTag[] $items */ 285 | $items = []; 286 | 287 | $slotCount = $this->petInventoryMenu->getInventory()->getSize(); 288 | for ($slot = 0; $slot < $slotCount; ++$slot) { 289 | $item = $this->petInventoryMenu->getInventory()->getItem($slot); 290 | if (!$item->isNull()) { 291 | $items[] = $item->nbtSerialize($slot); 292 | } 293 | } 294 | 295 | $this->saveInventory(new ListTag($items, NBT::TAG_Compound)); 296 | } 297 | 298 | return $nbt; 299 | } 300 | 301 | protected function entityBaseTick(int $tickDiff = 1): bool { 302 | if ($this->rider !== null or $this->isClosed()) { 303 | return parent::entityBaseTick($tickDiff); 304 | } 305 | 306 | $this->followOwner(); 307 | 308 | if ($this->checkVal <= 0) { 309 | $this->checkVal = 60; 310 | 311 | $owner = $this->getPetOwner(); 312 | 313 | if ($owner !== null) { 314 | $target = SimplePets::getInstance()->getPlayerByXuid($owner); 315 | 316 | if (($target !== null) && $this->getPosition()->distance($target->getPosition()) >= 20) { 317 | $this->teleport($target->getPosition()); 318 | } 319 | } 320 | } 321 | 322 | --$this->checkVal; 323 | return parent::entityBaseTick($tickDiff); 324 | } 325 | 326 | public function followOwner(): void { 327 | $owner = $this->getPetOwner(); 328 | 329 | if ($owner === null) { 330 | return; 331 | } 332 | 333 | $target = SimplePets::getInstance()->getPlayerByXuid($owner); 334 | 335 | if ($target === null) { 336 | return; 337 | } 338 | 339 | if ($this->getPosition()->distance($target->getPosition()) <= 2) { 340 | return; 341 | } 342 | 343 | if ($this->shouldJump()) { 344 | $this->jump(); 345 | } 346 | 347 | $x = $target->getLocation()->x - $this->getLocation()->x; 348 | $y = $target->getLocation()->y - $this->getLocation()->y; 349 | $z = $target->getLocation()->z - $this->getLocation()->z; 350 | /** @noinspection RandomApiMigrationInspection */ 351 | if ($x * $x + $z * $z < mt_rand(3, 8)) { 352 | $this->motion->x = 0; 353 | $this->motion->z = 0; 354 | } else { 355 | $this->motion->x = 0.17 * ($x / (abs($x) + abs($z))); 356 | $this->motion->z = 0.17 * ($z / (abs($x) + abs($z))); 357 | } 358 | 359 | $this->getLocation()->yaw = rad2deg(atan2(-$x, $z)); 360 | $this->getLocation()->pitch = rad2deg(-atan2($y, sqrt($x * $x + $z * $z))); 361 | 362 | $this->move($this->motion->x, $this->motion->y, $this->motion->z); 363 | $this->lookAt($target->getPosition()); 364 | 365 | $this->updateMovement(); 366 | } 367 | 368 | public function shouldJump(): bool { 369 | if ($this->getBlockInFront()->getId() !== BlockLegacyIds::AIR) { 370 | return $this->getBlockInFront(1)->getId() === BlockLegacyIds::AIR; 371 | } 372 | 373 | if ($this->getBlockInFront(-0.9) instanceof Carpet) { 374 | return !($this->getWorld()->getBlock($this->getPosition()->add(0, -0.9, 0)) instanceof Carpet); 375 | } 376 | 377 | if ($this->getBlockInFront() instanceof Flowable) { 378 | return false; 379 | } 380 | 381 | if ($this->getBlockInFront() instanceof Liquid) { 382 | return true; 383 | } 384 | 385 | return false; 386 | } 387 | 388 | public function getBlockInFront(float $y = 0): Block { 389 | $pos = $this->getPosition()->add($this->getDirectionVector()->x * $this->getScale(), $y, $this->getDirectionVector()->z * $this->getScale())->round(); 390 | return $this->getWorld()->getBlock($pos); 391 | } 392 | } --------------------------------------------------------------------------------