From 3e627701fb82accea04814ba51cad0f749084bd4 Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Tue, 19 Apr 2022 21:39:17 +0700 Subject: [PATCH 01/91] Added bonus for ingame function A tip to teleport since TP waypoint didn't work. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 11f694599..e39d4e70d 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,9 @@ There is a dummy user named "Server" in every player's friends list that you can `!clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including yellow rarity ones** from your inventory +### Bonus +When you want to teleport to somewhere, use the ingame marking function on Map, then named it `pos`, click Confirm. You will see your character falling from a very high destination, exact location that you marked. + # Quick Troubleshooting * If compiling wasnt successful, please check your JDK installation (must be JDK 8 and validated JDK's bin PATH variable) * My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is the issue, if using Fiddler make sure it running on another port except 8888 From 657ac2e5293d78875c213434b5c037755e9b64b5 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Tue, 19 Apr 2022 19:39:54 +0200 Subject: [PATCH 02/91] WIP Restart command - For now disconnects session, preferrably just send PlayerLoginRsp so it does the login sequence all over again --- data/query_cur_region.txt | 2 +- .../grasscutter/commands/PlayerCommands.java | 294 ++++++++++-------- 2 files changed, 169 insertions(+), 127 deletions(-) diff --git a/data/query_cur_region.txt b/data/query_cur_region.txt index 427c13f45..ddef1aff6 100644 --- a/data/query_cur_region.txt +++ b/data/query_cur_region.txt @@ -1 +1 @@ -GpgdCgw0Ny44OS4xNTIuNDcQ1awBGitodHRwczovL29zdXNhb2FzZXJ2ZXIueXVhbnNoZW4uY29tL3JlY2hhcmdlOgNVU0FCOWh0dHBzOi8vYXV0b3BhdGNoaGsueXVhbnNoZW4uY29tL2NsaWVudF9nYW1lX3Jlcy8yLjZfbGl2ZUo8aHR0cHM6Ly9hdXRvcGF0Y2hoay55dWFuc2hlbi5jb20vY2xpZW50X2Rlc2lnbl9kYXRhLzIuNl9saXZlUpIBaHR0cHM6Ly93ZWJzdGF0aWMtc2VhLmhveW92ZXJzZS5jb20veXMvZXZlbnQvaW0tc2VydmljZS9pbmRleC5odG1sP2ltX291dD1mYWxzZSZzaWduX3R5cGU9MiZhdXRoX2FwcGlkPWltX2NjcyZhdXRoa2V5X3Zlcj0xJndpbl9kaXJlY3Rpb249cG9ydHJhaXRiCDIuNl9saXZlcNnsmgOQAdnsmgOaAVx7InJlbW90ZU5hbWUiOiAiZGF0YV92ZXJzaW9ucyIsICJtZDUiOiAiMWE0NDVhZTQ4ZmJkYmUwMzgzMTJiZTg2OGYyODEzZDIiLCAiZmlsZVNpemUiOiA0NDE1faIBW3sicmVtb3RlTmFtZSI6ICJkYXRhX3ZlcnNpb25zIiwgIm1kNSI6ICIxNzU0MmM3YmJhYzQ5YjkxMWRlMjlhOTYyNzU0YmQ2MSIsICJmaWxlU2l6ZSI6IDUxNH2yAYEGCL23mQMa4AV7InJlbW90ZU5hbWUiOiAicmVzX3ZlcnNpb25zX2V4dGVybmFsIiwgIm1kNSI6ICI5ZjA5MmNhMTMwNjdjYWU0MzEzNDkzZWVkM2QzZTlhZSIsICJmaWxlU2l6ZSI6IDUyNjk2Nn0NCnsicmVtb3RlTmFtZSI6ICJyZXNfdmVyc2lvbnNfbWVkaXVtIiwgIm1kNSI6ICI3Yzk0MmU3MDRhODA0YzIxMjJmYzYzZWU5MjhlNzE0OCIsICJmaWxlU2l6ZSI6IDI4NTU1MH0NCnsicmVtb3RlTmFtZSI6ICJyZXNfdmVyc2lvbnNfc3RyZWFtaW5nIiwgIm1kNSI6ICJlMzVmNmQ4NTNkMDRjZTAyMmFjN2MzNmQ0M2Y0MGU3YyIsICJmaWxlU2l6ZSI6IDEyMjAzNn0NCnsicmVtb3RlTmFtZSI6ICJyZWxlYXNlX3Jlc192ZXJzaW9uc19leHRlcm5hbCIsICJtZDUiOiAiODc2NGIwZjJlZDgxNWNkNDQzMGUwODVjNDYxYTZmNGQiLCAiZmlsZVNpemUiOiA1MjY5NjZ9DQp7InJlbW90ZU5hbWUiOiAicmVsZWFzZV9yZXNfdmVyc2lvbnNfbWVkaXVtIiwgIm1kNSI6ICIxYmJlMzc0YzE2YmZmNDU5MTU5OWMyMTk3MzQyMDM0OCIsICJmaWxlU2l6ZSI6IDI4NTU1MH0NCnsicmVtb3RlTmFtZSI6ICJyZWxlYXNlX3Jlc192ZXJzaW9uc19zdHJlYW1pbmciLCAibWQ1IjogIjcyZmE3ZmY2NmQ1MTRmY2JhMzRhZjAwN2YyYjljMmY4IiwgImZpbGVTaXplIjogMTIyMDM2fQ0KeyJyZW1vdGVOYW1lIjogImJhc2VfcmV2aXNpb24iLCAibWQ1IjogImZiZmE2ZDZlMDcwMmQxYzc5ZTA1NjRmYjI4NjdlNzM4IiwgImZpbGVTaXplIjogMTh9IgEwKgo3YTM0YTM2YTZlMggyLjZfbGl2ZboBnBBFYzJiEAAAAJGCjvgHMrTsh3MtgsH5frMACAAAw460k//m++1MxIEXUCck/33uRkbXh2qC29/AivwLhKxa+XHVSAv0dKF5drsBoAKPy4OFKI9DJhCysPt3+RKmbXVvdgaktucaz4GU7/r1wurEHHyf+edEsopDvbCea4nbJVe9+qYHXwBPLepzFNymMWVp9eSkiySwB7aXOMLuWo7utTYk1t3BV+sc7C78f/aPIfGdD/s3XcTQzzEBPYu4FBG4D6PZ8oTGGvg0mWt5q/k2qmEcF8CdzUrJ38l/TiQuNSrWG3s/ALdDwXooplsCEl92sprxZswgpfKIsoPUuVSGUAIPOHY23+Yzx/j0AaMIUbeZB6mwGqffcNtW1qSbeeJWr/2HG9jbdBlr/wnPpDFdGn4oAzsuacaCYGMO8vkU20Lpwn7I3fce3H9zfmDqmroKE5d6tiB3+e212+jgft3b24tdudYGIbFVG7a3+DQYHtSDT5BOKQgbKs4Pw4Lks8vYprrIOHwfxHALjO6YlqkMJcbYPYYUCE3aitVgMFoLztZkVYEBSx6AQOEHG/PkpuDSnmhkDxvNzX8PvmwhEGWROY5kTaDm81bmLrMbf9AYBy5g2ZKP5Vvw6nLwfVrS7gesNPT30JZPHzrZCiUHB79eoWK6hSSlQX1b7z6/qkIFV06X+pgp2lqA4mauJYUm3sx6wmgeJaxDP/I6RpU1EiRFb3TzxkTGo3Wafp2PDg0q8sjj7A+xXREjf3WgsdwPXnCDByOLTV9u4Gd+7KwRZwK/9OChKNU5xXte4VQMf3TvNjBl07AQEtyBQ2swCO7YsiWx3EIv2oqy/zl1QEnVj8BVcfATwghu9vxY2oQ2J7ejxCEtIDRKJXFg8ziB7H1NaWknzlzDOC80UqfWKFCH8fC7KB3ysuAPpN4I0i1y8dRcZdiHK3aImhSs7GXDyr5BpG35RfZk1cBqIl1L8+mWZYKxXinKzKccYXl2JZFvkr+TWDjBoVIBDQBa1RjWPHGF3OT7KWiV6zsHchzVUe7re6WU7PhzRDTB/mw+kxsmcIuuMB2cTsbk29TgScQGd1IkQnzUPUtGYoQiv0i6JbAJFc0nMLl0tMds4xAPAefL2lqfTMIMbJufiohBBMvaEnchohkmDtrGGTuLvBWYSNtlSuSmC8GOlenCUG4bXMWM+ZHwnIA/GsmNsQR1wSlaXFNGnp/Qnw/q7I3Btyayp5Cf3PGLFMdho502/NcY5puROnctNAQKf4+5zUnfnhvqUK3DAWl9Ei5CtTUJ5Vopuh0HFTk0mEjgFutDHjQlXC3F3JWVpIUn22fZHlal2l/evB9sZVJxsMcEEoOrbMmHqmYd4JIdnLMhy9uPZCUoPc+LzrYFXXprNw0XPhFkI4ZtWM95Uz4NfuFWw3d+Ijd3szFMA3WvtZ9Hs1XbdI7sHrRGRIaGn9lNyygofOZhXHQC2ojbRyUOllJQYVUvRB18IxEIHitfD+Q6pUY7FM4TsYoSkoVIjJkQcIv4dsQLyG8StWSL4b4dJZEIi8tk9BQcY52/goO/FbvjB/Rz+cZd/r6J7akGskONwtUBlqe3i+PihvIUbfTA2AwRuaTgpvLwFkOxXRj4Uj0lTExB2to89IRkJ/eADa9eawoh0xswAUxHpmsiUoxRz3UF4gEzpVq4XmmOp2baE2+Uj4rtkzvH5dyODYO39FGudvwZwf6baOlFAKxyBfaVS+W4/udYnLKqoyT7fcuxOqx8cxxLZI6KuDs3nrOe/U9yxIgpvxZSZEejyWVvvRVTzKN3QzMorVXwoPyMlthJpFbAoRt+VPOwMUOAPo3Hhy/6iP03wmUUUBUf4SSSeYoOqXzvj60founyl3ugmpvdEMS2weyTUxq7gkR7xIfAnLzYsb1eLjy0oiRbHGu1QkwhKtqMwjuDfyOcUavzczbNizQHVVORfsiZTMuzLUufVkBug9LrmYkTkUgNs0dCkKQI3RsVP90Kix+gLYmbnIRAhlJbmgzbKULA9ipT3VU3aLXV5/o097pAvpKJGHsk8ibC3yCP2vf2mPLibBHIjGM1T0+sq682kjC1vsJWXl2JVMiDQbHW/zamNixPnU7uTriojCUYLTHt3M5D5d1IxIUIzWyTY6+9zFlO3BBZUCYOaqE0B4ncMfrV9rkuKLay69dPMQLuqF5MxD8oTA1HFDPUEvpFiUhNrxm2VSl7joxjf7TwYkLgDaDKzp5Cmbj4EdCvnRuZ8S+XtzremqnQkwfcbPxkS8GyQxVex9jxrBbz2dicI1hkfvi3hu8qqFJ87/Ozeg9RWjPpLd/Ax5tE6wCh6HO8FUMKO8gKMSFqaTM4i36AN8isOVm2jbKPfWm2Cx8Fwh4VdyKPJ/33FbPTn9dC9ox1/wcDHAta6wGqk224B3QJa6s+gKT1/qb+HObz1i9yRFEcneJkpkt6kTcws1kfoPeQUAwayy9p7Fj1l30TW9oIZwDdyA9746c/bR2dZt0zQtGXsynhjom87f9T2AfvK36EoWQA4hxiZvsKn6E+V4Q229t7FgP0j1oqF7iu+hlgl6IYkmzhf/LVdQJbGslS6Tpa/TpLsFXRnCX/rWBovU01jyvM0w7f7Yyc0S6NcCULtDH2Znqd3JKYAphuWOLWLQlDJg/CRSpK1x4V+hWNuTXETafVlk5ft9do6Et9krcFyHKzATI1SaapornUYv0rgL6hN9y+IZl1CyuL/uyWy9iqLmO5tjx9gitKlCT4Hcj6QWH6Wfg+zmAVraPVjvzCAU9odHRwczovL3dlYnN0YXRpYy1zZWEuaG95b3ZlcnNlLmNvbS95cy9ldmVudC9lMjAyMDA0MTBnb19jb21tdW5pdHkvaW5kZXguaHRtbCMv0gEKYWRmZWI4YmU3MdoBCmFkZmViOGJlNzH6AR1odHRwczovL2FjY291bnQuaG95b3ZlcnNlLmNvbYICcWh0dHBzOi8vaGs0ZS1hcGktb3MuaG95b3ZlcnNlLmNvbS9jb21tb24vYXBpY2RrZXkvYXBpL2V4Y2hhbmdlQ2RrZXk/c2lnbl90eXBlPTImYXV0aF9hcHBpZD1hcGljZGtleSZhdXRoa2V5X3Zlcj0xigJMaHR0cHM6Ly9hY2NvdW50LmhveW92ZXJzZS5jb20vIy9hYm91dC9wcml2YWN5SW5HYW1lP2FwcF9pZD00JmJpej1oazRlX2dsb2JhbFqcEEVjMmIQAAAAkYKO+AcytOyHcy2Cwfl+swAIAADDjrST/+b77UzEgRdQJyT/fe5GRteHaoLb38CK/AuErFr5cdVIC/R0oXl2uwGgAo/Lg4Uoj0MmELKw+3f5EqZtdW92BqS25xrPgZTv+vXC6sQcfJ/550SyikO9sJ5ridslV736pgdfAE8t6nMU3KYxZWn15KSLJLAHtpc4wu5aju61NiTW3cFX6xzsLvx/9o8h8Z0P+zddxNDPMQE9i7gUEbgPo9nyhMYa+DSZa3mr+TaqYRwXwJ3NSsnfyX9OJC41KtYbez8At0PBeiimWwISX3aymvFmzCCl8oiyg9S5VIZQAg84djbf5jPH+PQBowhRt5kHqbAap99w21bWpJt54lav/Ycb2Nt0GWv/Cc+kMV0afigDOy5pxoJgYw7y+RTbQunCfsjd9x7cf3N+YOqaugoTl3q2IHf57bXb6OB+3dvbi1251gYhsVUbtrf4NBge1INPkE4pCBsqzg/DguSzy9imusg4fB/EcAuM7piWqQwlxtg9hhQITdqK1WAwWgvO1mRVgQFLHoBA4Qcb8+Sm4NKeaGQPG83Nfw++bCEQZZE5jmRNoObzVuYusxt/0BgHLmDZko/lW/DqcvB9WtLuB6w09PfQlk8fOtkKJQcHv16hYrqFJKVBfVvvPr+qQgVXTpf6mCnaWoDiZq4lhSbezHrCaB4lrEM/8jpGlTUSJEVvdPPGRMajdZp+nY8ODSryyOPsD7FdESN/daCx3A9ecIMHI4tNX27gZ37srBFnAr/04KEo1TnFe17hVAx/dO82MGXTsBAS3IFDazAI7tiyJbHcQi/airL/OXVASdWPwFVx8BPCCG72/FjahDYnt6PEIS0gNEolcWDzOIHsfU1paSfOXMM4LzRSp9YoUIfx8LsoHfKy4A+k3gjSLXLx1Fxl2IcrdoiaFKzsZcPKvkGkbflF9mTVwGoiXUvz6ZZlgrFeKcrMpxxheXYlkW+Sv5NYOMGhUgENAFrVGNY8cYXc5PspaJXrOwdyHNVR7ut7pZTs+HNENMH+bD6TGyZwi64wHZxOxuTb1OBJxAZ3UiRCfNQ9S0ZihCK/SLolsAkVzScwuXS0x2zjEA8B58vaWp9Mwgxsm5+KiEEEy9oSdyGiGSYO2sYZO4u8FZhI22VK5KYLwY6V6cJQbhtcxYz5kfCcgD8ayY2xBHXBKVpcU0aen9CfD+rsjcG3JrKnkJ/c8YsUx2GjnTb81xjmm5E6dy00BAp/j7nNSd+eG+pQrcMBaX0SLkK1NQnlWim6HQcVOTSYSOAW60MeNCVcLcXclZWkhSfbZ9keVqXaX968H2xlUnGwxwQSg6tsyYeqZh3gkh2csyHL249kJSg9z4vOtgVdems3DRc+EWQjhm1Yz3lTPg1+4VbDd34iN3ezMUwDda+1n0ezVdt0juwetEZEhoaf2U3LKCh85mFcdALaiNtHJQ6WUlBhVS9EHXwjEQgeK18P5DqlRjsUzhOxihKShUiMmRBwi/h2xAvIbxK1ZIvhvh0lkQiLy2T0FBxjnb+Cg78Vu+MH9HP5xl3+vontqQayQ43C1QGWp7eL4+KG8hRt9MDYDBG5pOCm8vAWQ7FdGPhSPSVMTEHa2jz0hGQn94ANr15rCiHTGzABTEemayJSjFHPdQXiATOlWrheaY6nZtoTb5SPiu2TO8fl3I4Ng7f0Ua52/BnB/pto6UUArHIF9pVL5bj+51icsqqjJPt9y7E6rHxzHEtkjoq4Ozees579T3LEiCm/FlJkR6PJZW+9FVPMo3dDMyitVfCg/IyW2EmkVsChG35U87AxQ4A+jceHL/qI/TfCZRRQFR/hJJJ5ig6pfO+PrR+i6fKXe6Cam90QxLbB7JNTGruCRHvEh8CcvNixvV4uPLSiJFsca7VCTCEq2ozCO4N/I5xRq/NzNs2LNAdVU5F+yJlMy7MtS59WQG6D0uuZiRORSA2zR0KQpAjdGxU/3QqLH6AtiZuchECGUluaDNspQsD2KlPdVTdotdXn+jT3ukC+kokYeyTyJsLfII/a9/aY8uJsEciMYzVPT6yrrzaSMLW+wlZeXYlUyINBsdb/NqY2LE+dTu5OuKiMJRgtMe3czkPl3UjEhQjNbJNjr73MWU7cEFlQJg5qoTQHidwx+tX2uS4otrLr108xAu6oXkzEPyhMDUcUM9QS+kWJSE2vGbZVKXuOjGN/tPBiQuANoMrOnkKZuPgR0K+dG5nxL5e3Ot6aqdCTB9xs/GRLwbJDFV7H2PGsFvPZ2JwjWGR++LeG7yqoUnzv87N6D1FaM+kt38DHm0TrAKHoc7wVQwo7yAoxIWppMziLfoA3yKw5WbaNso99abYLHwXCHhV3Io8n/fcVs9Of10L2jHX/BwMcC1rrAaqTbbgHdAlrqz6ApPX+pv4c5vPWL3JEURyd4mSmS3qRNzCzWR+g95BQDBrLL2nsWPWXfRNb2ghnAN3ID3vjpz9tHZ1m3TNC0ZezKeGOibzt/1PYB+8rfoShZADiHGJm+wqfoT5XhDbb23sWA/SPWioXuK76GWCXohiSbOF/8tV1AlsayVLpOlr9OkuwVdGcJf+tYGi9TTWPK8zTDt/tjJzRLo1wJQu0MfZmep3ckpgCmG5Y4tYtCUMmD8JFKkrXHhX6FY25NcRNp9WWTl+312joS32StwXIcrMBMjVJpqmiudRi/SuAvqE33L4hmXULK4v+7JbL2KouY7m2PH2CK0qUJPgdyPpBYfpZ+D7OYBWto9WO/GLVArJ6MMs9QA4I3dIIwrylWFU1xRNtPj3ZUiooCQGiArlYzrMmLb09eDW0QedOr3CPLOlcmZroIV9XmnD9YYJSBxv5L8mbNaGWcZkxZM04GrPcOeDTN2pXq/DdB2cHRC0nT6YaQhvAnvVDFhnoBHnJS9B/aGcgHo3mVzWBi2jeeXNyLZaHhkADBRCPGmTwonHJPIig0hnMvCbLB7b5qHpN8uaQBDW4T6cqIZljsbbXUY1maAcu5vmLK8Tq7Vmu74TTQzghjgxVHB6vIS8vhqWKOKyGSrDGLDnGlcHFRlH/Omz49L2EBfYyDPKUBsi+VMP+cvIrky3XqoBUtTuQwXmcCF3LxPyf8SCVwFZSgDHMND7owHO90vRS60lwsO9QkfojEmbs0MTvFrB/FM1CeHxQeSBA74JVmDRNL4efGIopyTt6SltAxc0flyQzHwW4D8oWg6Tm/Na6 \ No newline at end of file +GpgdCgo4LjIwOS42Ni4xENWsARosaHR0cHM6Ly9vc2V1cm9vYXNlcnZlci55dWFuc2hlbi5jb20vcmVjaGFyZ2U6BGV1cm9COWh0dHBzOi8vYXV0b3BhdGNoaGsueXVhbnNoZW4uY29tL2NsaWVudF9nYW1lX3Jlcy8yLjZfbGl2ZUo8aHR0cHM6Ly9hdXRvcGF0Y2hoay55dWFuc2hlbi5jb20vY2xpZW50X2Rlc2lnbl9kYXRhLzIuNl9saXZlUpIBaHR0cHM6Ly93ZWJzdGF0aWMtc2VhLmhveW92ZXJzZS5jb20veXMvZXZlbnQvaW0tc2VydmljZS9pbmRleC5odG1sP2ltX291dD1mYWxzZSZzaWduX3R5cGU9MiZhdXRoX2FwcGlkPWltX2NjcyZhdXRoa2V5X3Zlcj0xJndpbl9kaXJlY3Rpb249cG9ydHJhaXRiCDIuNl9saXZlcNnsmgOQAdnsmgOaAVx7InJlbW90ZU5hbWUiOiAiZGF0YV92ZXJzaW9ucyIsICJtZDUiOiAiMWE0NDVhZTQ4ZmJkYmUwMzgzMTJiZTg2OGYyODEzZDIiLCAiZmlsZVNpemUiOiA0NDE1faIBW3sicmVtb3RlTmFtZSI6ICJkYXRhX3ZlcnNpb25zIiwgIm1kNSI6ICIxNzU0MmM3YmJhYzQ5YjkxMWRlMjlhOTYyNzU0YmQ2MSIsICJmaWxlU2l6ZSI6IDUxNH2yAYEGCL23mQMa4AV7InJlbW90ZU5hbWUiOiAicmVzX3ZlcnNpb25zX2V4dGVybmFsIiwgIm1kNSI6ICI5ZjA5MmNhMTMwNjdjYWU0MzEzNDkzZWVkM2QzZTlhZSIsICJmaWxlU2l6ZSI6IDUyNjk2Nn0NCnsicmVtb3RlTmFtZSI6ICJyZXNfdmVyc2lvbnNfbWVkaXVtIiwgIm1kNSI6ICI3Yzk0MmU3MDRhODA0YzIxMjJmYzYzZWU5MjhlNzE0OCIsICJmaWxlU2l6ZSI6IDI4NTU1MH0NCnsicmVtb3RlTmFtZSI6ICJyZXNfdmVyc2lvbnNfc3RyZWFtaW5nIiwgIm1kNSI6ICJlMzVmNmQ4NTNkMDRjZTAyMmFjN2MzNmQ0M2Y0MGU3YyIsICJmaWxlU2l6ZSI6IDEyMjAzNn0NCnsicmVtb3RlTmFtZSI6ICJyZWxlYXNlX3Jlc192ZXJzaW9uc19leHRlcm5hbCIsICJtZDUiOiAiODc2NGIwZjJlZDgxNWNkNDQzMGUwODVjNDYxYTZmNGQiLCAiZmlsZVNpemUiOiA1MjY5NjZ9DQp7InJlbW90ZU5hbWUiOiAicmVsZWFzZV9yZXNfdmVyc2lvbnNfbWVkaXVtIiwgIm1kNSI6ICIxYmJlMzc0YzE2YmZmNDU5MTU5OWMyMTk3MzQyMDM0OCIsICJmaWxlU2l6ZSI6IDI4NTU1MH0NCnsicmVtb3RlTmFtZSI6ICJyZWxlYXNlX3Jlc192ZXJzaW9uc19zdHJlYW1pbmciLCAibWQ1IjogIjcyZmE3ZmY2NmQ1MTRmY2JhMzRhZjAwN2YyYjljMmY4IiwgImZpbGVTaXplIjogMTIyMDM2fQ0KeyJyZW1vdGVOYW1lIjogImJhc2VfcmV2aXNpb24iLCAibWQ1IjogImZiZmE2ZDZlMDcwMmQxYzc5ZTA1NjRmYjI4NjdlNzM4IiwgImZpbGVTaXplIjogMTh9IgEwKgo3YTM0YTM2YTZlMggyLjZfbGl2ZboBnBBFYzJiEAAAAJNvNOvzlkCCDSqpQ6a141IACAAA6gq2poqqrhWr1LS/wULjaiSPJIGsouCxUfY40ezmGoMU5SZZLwQ97KrlkCLKvTVycxteFwEPDxFrKxiHE1oigrAAkjc0NU12JqcQBFL8ExWeR+3QfCPnh7MWo424stJoHPADl9E6R/n3YDXAtq1gUzZu5Y4aGtd9XDyVjozcbIrtVVTGVpvRIuwGYoOCRCwDeRKphu9MoJfbi9mawLh5XSq+KLsAksjM90JJ/DEUzP2XCB/QILsiSiwbET5LUrl65OXCN4sLxZg+86qmeU28cdz4tWDewXYFO+Y7AnJAt7JfpgR/8Os7A9CDPD8WA6GBdqyplmoKRtnjjZG9ZGZIk1YF7AdGUhE8672XlWW3clJaNMBpHFkON+t7Utgu6prY/uJJLFZlGm5KMSend8u8GTMYOE5/AJsVkX/5eS8V2F6Dt6mZJgPtGAlX7Rp1a1R57FMKQLS+9nIzKDMclWFs7ebbnv+lcqckuqln19/JMrYQg9E4IiDVn5akaHZbnYBw0+HDR5kfT2xWfWVo8CJu4mPSpVR1kI4HhZXTURnHa7ezObuwHQm3NS7wHK7VO0E+qgnUgb+vK9M0cHTQE6FsCi/bk0VaHZtDxpMCTfKluJiOsxhvRePOjEVyNyLwLJaoxwwSukMfSH9G+q62ygFmmErAQfKKWLreb72UegOgmhD8T8aytvWXHkWk07QCttqgPhax8BAW2OvRJluvorBUIeHDeO6QeBaZns4EXIYUcIQlfi3yGsLKhhGLb2OgN6a6B3ElxdgXRRYMGAdDoAxEnCcWmdZx874vEvf2KFUP6aU8l4lh0XV6RU/D7A+eqWN5bH4ffS4QBQq2MU6CNA5XumMsD4zUfC6od5T7Tt7uQsgbqtIMgXKpO8lRk4pRgnpPsqM1Ou8kTb1UdQSc4yREuJlrL2Tmtf35cAw7W290LIO/GaO9Rj1CPhATMn7jbUTA6+QN7rTxIzbVl66k95wQth81TYuvw1DlGOpVDTbcCB0uvfmcAAGhj57jVBlyVIu+KJzrrx8z9Uh6scsMCgrMPJn7nsCHSXD6yElTgLrF7FVwgqsxDjgcquqkrSnknP92jb+11IJbw05Ass4hcMRGJxdAefSWDIgdi7l4GnppPdUvLkG5uvBlO85AiT2NpqNmShebfst8rQLFc1B7hAcvh9EpM9Sii0/XXfe9tEf7AwKj2SWmS79PsjEiAiv18tJ6gDlaJ0WFSchXNBRPu8ESvKlE8q7myuc2t5nnxv/hctkez5+nh9SgQ3beL34chSL4RPTH32Jqzs5p7s2n87Bv4vfQccYFAF+kKMUZzKnWKl0LCrStSd3+s+4KF/tZXEKue5KoVobNhaQrD33HjWf9Rvw0ULd4ho4A6wj/uga9o7rZ2C2v1YEDNNqZ5/v/udN3hl7tXrWv5UXcUyTtooa66sJ8oITE8+4aDWimtX6dy0CdFQj2mHppbbvRF66flDd/gFA35xQOXfYdOud3NJogglHXofOoB0LEdbao9C6Nt2v+z7C4S0l3cAMXp/yiI9qxJT9b0mQ2zp2GN5i4gvrp+6iKjqxf+IA++oB/JzbbpSVOFJmFSysv4v3Al2AVbmFycYqv0GaoiZ22wiu8Ok3+LCyKJTITtaJLAtgbpwRfa9SUkdpRwMK1vMN1s6jZU9gdejY3oLVKjFpg66c9bagpmvIG1/gfgY9yT++Y8img4aB/JDJMAS2MVGxGlyrRFGaXBWLq3SkhqDqGD5klbvYv2IFMr4BAHP2uwLb92qEhtlksiVQYk8HGxLWlU2Fo+Pxee8L1xvQOgPdY4/cb33BuJXvtyW5ea5EmVtBPo4MU1ws9BvAzLs13Nisl+/FBvBn8ktkZmE5e6nsdpGEtq4/d7MfCLXGRI5L730mIieeAvtQcb0NMWGcubHPgjY58Pv4MSdRpOQakzM2rA5UD5O57rGH5q6p3HZfZk2iPUcJRsebMKJ4l1omr5JeD95DKDKy7Cts3RajufslekL3/wHwUZbAdEWyux2w1zbiukmhTfh0nbenm8Q8KOASCDo0SWO3e9FpOz5o+phHBVYgmfxRt11OonnLt1qKB7j/a7YdufvkFSsFm4UdgsJMPIHzeBjbSSDlLeCdmdKGQtFLC73npe6efYGUupMIV9EYup1D/OoCNlz2r/FhJ7aLRtj6Q6cb161MLp+rmjbSzN+RVFwf7wAVpLQMrHwTvUzB/8M7cTjN5VFE583uhy4KKf+W3iTxzdyX2SAD9QVu6EXv4KaEFBy8Vo0sga907Fi7imkwgjY2cdnEtPMMeO2Jqj3yWPdqrlVtfAn1jd14oix7YObsJ8mVBeueiplG9d7dxcA1GV+7xX9wOyPDcfH5FBAIlglBIEhjSyNQErH+JYRbOUotXMBQa1tlRRtBSLLDCRgEpG8F+Dv5Oao9ZBnKAi0GRjPKo+OjsehWzrNXGDcoTQsGD/bmKpCdaUOuEa6rLA7tbYwqT2SPdCJzBx2J+kI1bwDWhFF9ROrh9MvmwvMBE9dH5mbQj78p2P5gar2BUcNNbSVvSgxtCa1NHsf/GwrPmTQLPxCrEBcDucxIpsqfINp48iCJGZ4NvlRIZolmaVBWlxjVl/XYcb2YOl3K48e+LsfblTU6tyneZimrS+Y0qt7lncte6NZGPnf98wNLxY39IX8gATezGXoZ03TOhy1jX3epf4Bfw5ZyvU8/XJ2BvbPpw+b8LgS0y0DkeQG4mQGlHidnCAU9odHRwczovL3dlYnN0YXRpYy1zZWEuaG95b3ZlcnNlLmNvbS95cy9ldmVudC9lMjAyMDA0MTBnb19jb21tdW5pdHkvaW5kZXguaHRtbCMv0gEKYWRmZWI4YmU3MdoBCmFkZmViOGJlNzH6AR1odHRwczovL2FjY291bnQuaG95b3ZlcnNlLmNvbYICcWh0dHBzOi8vaGs0ZS1hcGktb3MuaG95b3ZlcnNlLmNvbS9jb21tb24vYXBpY2RrZXkvYXBpL2V4Y2hhbmdlQ2RrZXk/c2lnbl90eXBlPTImYXV0aF9hcHBpZD1hcGljZGtleSZhdXRoa2V5X3Zlcj0xigJMaHR0cHM6Ly9hY2NvdW50LmhveW92ZXJzZS5jb20vIy9hYm91dC9wcml2YWN5SW5HYW1lP2FwcF9pZD00JmJpej1oazRlX2dsb2JhbFqcEEVjMmIQAAAAk2806/OWQIINKqlDprXjUgAIAADqCramiqquFavUtL/BQuNqJI8kgayi4LFR9jjR7OYagxTlJlkvBD3squWQIsq9NXJzG14XAQ8PEWsrGIcTWiKCsACSNzQ1TXYmpxAEUvwTFZ5H7dB8I+eHsxajjbiy0mgc8AOX0TpH+fdgNcC2rWBTNm7ljhoa131cPJWOjNxsiu1VVMZWm9Ei7AZig4JELAN5EqmG70ygl9uL2ZrAuHldKr4ouwCSyMz3Qkn8MRTM/ZcIH9AguyJKLBsRPktSuXrk5cI3iwvFmD7zqqZ5Tbxx3Pi1YN7BdgU75jsCckC3sl+mBH/w6zsD0IM8PxYDoYF2rKmWagpG2eONkb1kZkiTVgXsB0ZSETzrvZeVZbdyUlo0wGkcWQ4363tS2C7qmtj+4kksVmUabkoxJ6d3y7wZMxg4Tn8AmxWRf/l5LxXYXoO3qZkmA+0YCVftGnVrVHnsUwpAtL72cjMoMxyVYWzt5tue/6VypyS6qWfX38kythCD0TgiINWflqRodludgHDT4cNHmR9PbFZ9ZWjwIm7iY9KlVHWQjgeFldNRGcdrt7M5u7AdCbc1LvAcrtU7QT6qCdSBv68r0zRwdNAToWwKL9uTRVodm0PGkwJN8qW4mI6zGG9F486MRXI3IvAslqjHDBK6Qx9If0b6rrbKAWaYSsBB8opYut5vvZR6A6CaEPxPxrK29ZceRaTTtAK22qA+FrHwEBbY69EmW6+isFQh4cN47pB4FpmezgRchhRwhCV+LfIawsqGEYtvY6A3proHcSXF2BdFFgwYB0OgDEScJxaZ1nHzvi8S9/YoVQ/ppTyXiWHRdXpFT8PsD56pY3lsfh99LhAFCrYxToI0Dle6YywPjNR8Lqh3lPtO3u5CyBuq0gyBcqk7yVGTilGCek+yozU67yRNvVR1BJzjJES4mWsvZOa1/flwDDtbb3Qsg78Zo71GPUI+EBMyfuNtRMDr5A3utPEjNtWXrqT3nBC2HzVNi6/DUOUY6lUNNtwIHS69+ZwAAaGPnuNUGXJUi74onOuvHzP1SHqxywwKCsw8mfuewIdJcPrISVOAusXsVXCCqzEOOByq6qStKeSc/3aNv7XUglvDTkCyziFwxEYnF0B59JYMiB2LuXgaemk91S8uQbm68GU7zkCJPY2mo2ZKF5t+y3ytAsVzUHuEBy+H0Skz1KKLT9dd9720R/sDAqPZJaZLv0+yMSICK/Xy0nqAOVonRYVJyFc0FE+7wRK8qUTyrubK5za3mefG/+Fy2R7Pn6eH1KBDdt4vfhyFIvhE9MffYmrOzmnuzafzsG/i99BxxgUAX6QoxRnMqdYqXQsKtK1J3f6z7goX+1lcQq57kqhWhs2FpCsPfceNZ/1G/DRQt3iGjgDrCP+6Br2jutnYLa/VgQM02pnn+/+503eGXu1eta/lRdxTJO2ihrrqwnyghMTz7hoNaKa1fp3LQJ0VCPaYemltu9EXrp+UN3+AUDfnFA5d9h0653c0miCCUdeh86gHQsR1tqj0Lo23a/7PsLhLSXdwAxen/KIj2rElP1vSZDbOnYY3mLiC+un7qIqOrF/4gD76gH8nNtulJU4UmYVLKy/i/cCXYBVuYXJxiq/QZqiJnbbCK7w6Tf4sLIolMhO1oksC2BunBF9r1JSR2lHAwrW8w3WzqNlT2B16NjegtUqMWmDrpz1tqCma8gbX+B+Bj3JP75jyKaDhoH8kMkwBLYxUbEaXKtEUZpcFYurdKSGoOoYPmSVu9i/YgUyvgEAc/a7Atv3aoSG2WSyJVBiTwcbEtaVTYWj4/F57wvXG9A6A91jj9xvfcG4le+3Jbl5rkSZW0E+jgxTXCz0G8DMuzXc2KyX78UG8GfyS2RmYTl7qex2kYS2rj93sx8ItcZEjkvvfSYiJ54C+1BxvQ0xYZy5sc+CNjnw+/gxJ1Gk5BqTMzasDlQPk7nusYfmrqncdl9mTaI9RwlGx5swoniXWiavkl4P3kMoMrLsK2zdFqO5+yV6Qvf/AfBRlsB0RbK7HbDXNuK6SaFN+HSdt6ebxDwo4BIIOjRJY7d70Wk7Pmj6mEcFViCZ/FG3XU6iecu3WooHuP9rth25++QVKwWbhR2Cwkw8gfN4GNtJIOUt4J2Z0oZC0UsLveel7p59gZS6kwhX0Ri6nUP86gI2XPav8WEntotG2PpDpxvXrUwun6uaNtLM35FUXB/vABWktAysfBO9TMH/wztxOM3lUUTnze6HLgop/5beJPHN3JfZIAP1BW7oRe/gpoQUHLxWjSyBr3TsWLuKaTCCNjZx2cS08wx47YmqPfJY92quVW18CfWN3XiiLHtg5uwnyZUF656KmUb13t3FwDUZX7vFf3A7I8Nx8fkUEAiWCUEgSGNLI1ASsf4lhFs5Si1cwFBrW2VFG0FIssMJGASkbwX4O/k5qj1kGcoCLQZGM8qj46Ox6FbOs1cYNyhNCwYP9uYqkJ1pQ64RrqssDu1tjCpPZI90InMHHYn6QjVvANaEUX1E6uH0y+bC8wET10fmZtCPvynY/mBqvYFRw01tJW9KDG0JrU0ex/8bCs+ZNAs/EKsQFwO5zEimyp8g2njyIIkZng2+VEhmiWZpUFaXGNWX9dhxvZg6Xcrjx74ux9uVNTq3Kd5mKatL5jSq3uWdy17o1kY+d/3zA0vFjf0hfyABN7MZehnTdM6HLWNfd6l/gF/DlnK9Tz9cnYG9s+nD5vwuBLTLQOR5AbiZAaUeJ2WLVAtaPymwUgxn8nMUMGk2pDMbkJNLgHPcao2F2HLBdC2W3r1Qs5PtDbMuMCIPSYscVx1x66qcJw19SiQyNLxCY9ErLGDY4mChY/X5NX7Pc2ricYE7EzCSZMYQmtUVZoqn1RPGz1P9Gj65Edm70zFOfRWfR+sTboONM7W3oNk1mkI2M5GwQrQpkAiyb5zwdxpsOwtQ0s0Sin4IOonpW9KcRv8yB8rMOMu6+C6m4h2MwRPj6QrVPWd8PV22qB4iAwfV3UYpjo91Mw/V5xT1DRSrb65vYtu8E4lR3HMv37at4aV6v8RluqeAIHHDJBANaUN3eLCSHewfEb+osQ5BV7XQT5Dwdy/LLFo+hdCBp0Retgtiwsq3+EgRs9i/d9tmghRqEdD/6re752aRiyTsf7CkWY6O7cCGfvmOLE0XhNQra+tLnlJCR1y4m1LOTB3ulQnZrA2E7Mmk7 \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index b6401740d..57976381a 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -21,6 +21,7 @@ import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; +import emu.grasscutter.server.packet.send.PacketPlayerLoginRsp; import emu.grasscutter.utils.Position; import java.util.LinkedList; @@ -30,15 +31,15 @@ import java.util.List; * A container for player-related commands. */ public final class PlayerCommands { - @Command(label = "give", aliases = {"g", "item", "giveitem"}, - usage = "Usage: give [player] [amount]") + @Command(label = "give", aliases = { "g", "item", + "giveitem" }, usage = "Usage: give [player] [amount]") public static class GiveCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { int target, item, amount = 1; - switch(args.size()) { + switch (args.size()) { default: CommandHandler.sendMessage(player, "Usage: give [amount]"); return; @@ -56,8 +57,9 @@ public final class PlayerCommands { try { target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - target = player.getUid(); amount = Integer.parseInt(args.get(1)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + target = player.getUid(); + amount = Integer.parseInt(args.get(1)); item = Integer.parseInt(args.get(0)); } else { item = Integer.parseInt(args.get(1)); @@ -71,9 +73,10 @@ public final class PlayerCommands { case 3: try { target = Integer.parseInt(args.get(0)); - - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - CommandHandler.sendMessage(player, "Invalid player ID."); return; + + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + CommandHandler.sendMessage(player, "Invalid player ID."); + return; } item = Integer.parseInt(args.get(1)); @@ -88,23 +91,26 @@ public final class PlayerCommands { GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); return; + if (targetPlayer == null) { + CommandHandler.sendMessage(player, "Player not found."); + return; } ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(player, "Invalid item id."); return; + if (itemData == null) { + CommandHandler.sendMessage(player, "Invalid item id."); + return; } - + this.item(targetPlayer, itemData, amount); } /** * give [player] [itemId|itemName] [amount] */ - @Override public void execute(List args) { - if(args.size() < 2) { + @Override + public void execute(List args) { + if (args.size() < 2) { CommandHandler.sendMessage(null, "Usage: give [amount]"); return; } @@ -112,32 +118,37 @@ public final class PlayerCommands { try { int target = Integer.parseInt(args.get(0)); int item = Integer.parseInt(args.get(1)); - int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); - + int amount = 1; + if (args.size() > 2) + amount = Integer.parseInt(args.get(2)); + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; + if (targetPlayer == null) { + CommandHandler.sendMessage(null, "Player not found."); + return; } - + ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(null, "Invalid item id."); return; + if (itemData == null) { + CommandHandler.sendMessage(null, "Invalid item id."); + return; } - + this.item(targetPlayer, itemData, amount); } catch (NumberFormatException ignored) { CommandHandler.sendMessage(null, "Invalid item or player ID."); } } - + private void item(GenshinPlayer player, ItemData itemData, int amount) { GenshinItem genshinItem = new GenshinItem(itemData); - if(itemData.isEquip()) { + if (itemData.isEquip()) { List items = new LinkedList<>(); - for(int i = 0; i < amount; i++) { + for (int i = 0; i < amount; i++) { items.add(genshinItem); - } player.getInventory().addItems(items); + } + player.getInventory().addItems(items); player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); } else { genshinItem.setCount(amount); @@ -146,37 +157,41 @@ public final class PlayerCommands { } } } - - @Command(label = "drop", aliases = {"d", "dropitem"}, - usage = "Usage: drop [amount]", - execution = Command.Execution.PLAYER) + + @Command(label = "drop", aliases = { "d", + "dropitem" }, usage = "Usage: drop [amount]", execution = Command.Execution.PLAYER) public static class DropCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { + if (args.size() < 1) { CommandHandler.sendMessage(player, "Usage: drop [amount]"); return; } try { int item = Integer.parseInt(args.get(0)); - int amount = 1; if(args.size() > 1) amount = Integer.parseInt(args.get(1)); + int amount = 1; + if (args.size() > 1) + amount = Integer.parseInt(args.get(1)); ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(player, "Invalid item id."); return; + if (itemData == null) { + CommandHandler.sendMessage(player, "Invalid item id."); + return; } if (itemData.isEquip()) { float range = (5f + (.1f * amount)); for (int i = 0; i < amount; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)) + .addY(3f).addZ((float) (Math.random() * range) - (range / 2)); EntityItem entity = new EntityItem(player.getScene(), player, itemData, pos, 1); player.getScene().addEntity(entity); } } else { - EntityItem entity = new EntityItem(player.getScene(), player, itemData, player.getPos().clone().addY(3f), amount); + EntityItem entity = new EntityItem(player.getScene(), player, itemData, + player.getPos().clone().addY(3f), amount); player.getScene().addEntity(entity); } } catch (NumberFormatException ignored) { @@ -185,26 +200,27 @@ public final class PlayerCommands { } } - @Command(label = "givechar", aliases = {"givec"}, - usage = "Usage: givechar [level|avatarId] [level]") + @Command(label = "givechar", aliases = { + "givec" }, usage = "Usage: givechar [level|avatarId] [level]") public static class GiveCharCommand implements CommandHandler { - @Override public void execute(GenshinPlayer player, List args) { + @Override + public void execute(GenshinPlayer player, List args) { int target, avatarId, level = 1, ascension = 1; - if(args.size() < 1) { + if (args.size() < 1) { CommandHandler.sendMessage(player, "Usage: givechar [level]"); return; } - - switch(args.size()) { + + switch (args.size()) { default: - CommandHandler.sendMessage(player, "Usage: givechar [level]"); + CommandHandler.sendMessage(player, "Usage: givechar [level]"); return; case 2: try { target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - target = player.getUid(); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + target = player.getUid(); level = Integer.parseInt(args.get(1)); avatarId = Integer.parseInt(args.get(0)); } else { @@ -219,8 +235,9 @@ public final class PlayerCommands { case 3: try { target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - CommandHandler.sendMessage(player, "Invalid player ID."); return; + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + CommandHandler.sendMessage(player, "Invalid player ID."); + return; } avatarId = Integer.parseInt(args.get(1)); @@ -234,13 +251,15 @@ public final class PlayerCommands { } GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); return; + if (targetPlayer == null) { + CommandHandler.sendMessage(player, "Player not found."); + return; } - + AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId); - if(avatarData == null) { - CommandHandler.sendMessage(player, "Invalid avatar id."); return; + if (avatarData == null) { + CommandHandler.sendMessage(player, "Invalid avatar id."); + return; } // Calculate ascension level. @@ -253,16 +272,16 @@ public final class PlayerCommands { GenshinAvatar avatar = new GenshinAvatar(avatarId); avatar.setLevel(level); avatar.setPromoteLevel(ascension); - + // This will handle stats and talents avatar.recalcStats(); - + targetPlayer.addAvatar(avatar); } @Override public void execute(List args) { - if(args.size() < 2) { + if (args.size() < 2) { CommandHandler.sendMessage(null, "Usage: givechar [amount]"); return; } @@ -270,33 +289,37 @@ public final class PlayerCommands { try { int target = Integer.parseInt(args.get(0)); int avatarID = Integer.parseInt(args.get(1)); - int level = 1; if(args.size() > 2) level = Integer.parseInt(args.get(2)); + int level = 1; + if (args.size() > 2) + level = Integer.parseInt(args.get(2)); int ascension; - + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; + if (targetPlayer == null) { + CommandHandler.sendMessage(null, "Player not found."); + return; } - + AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarID); - if(avatarData == null) { - CommandHandler.sendMessage(null, "Invalid avatar id."); return; + if (avatarData == null) { + CommandHandler.sendMessage(null, "Invalid avatar id."); + return; } - + // Calculate ascension level. if (level <= 40) { ascension = (int) Math.ceil(level / 20f); } else { ascension = (int) Math.ceil(level / 10f) - 3; } - + GenshinAvatar avatar = new GenshinAvatar(avatarID); avatar.setLevel(level); avatar.setPromoteLevel(ascension); // This will handle stats and talents avatar.recalcStats(); - + targetPlayer.addAvatar(avatar); } catch (NumberFormatException ignored) { CommandHandler.sendMessage(null, "Invalid item or player ID."); @@ -304,30 +327,35 @@ public final class PlayerCommands { } } - @Command(label = "spawn", execution = Command.Execution.PLAYER, - usage = "Usage: spawn [level] [amount]") + @Command(label = "spawn", execution = Command.Execution.PLAYER, usage = "Usage: spawn [level] [amount]") public static class SpawnCommand implements CommandHandler { - + @Override public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { + if (args.size() < 1) { CommandHandler.sendMessage(null, "Usage: spawn [amount]"); return; } try { int entity = Integer.parseInt(args.get(0)); - int level = 1; if(args.size() > 1) level = Integer.parseInt(args.get(1)); - int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); + int level = 1; + if (args.size() > 1) + level = Integer.parseInt(args.get(1)); + int amount = 1; + if (args.size() > 2) + amount = Integer.parseInt(args.get(2)); MonsterData entityData = GenshinData.getMonsterDataMap().get(entity); - if(entityData == null) { - CommandHandler.sendMessage(null, "Invalid entity id."); return; + if (entityData == null) { + CommandHandler.sendMessage(null, "Invalid entity id."); + return; } float range = (5f + (.1f * amount)); for (int i = 0; i < amount; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f) + .addZ((float) (Math.random() * range) - (range / 2)); EntityMonster monster = new EntityMonster(player.getScene(), entityData, pos, level); player.getScene().addEntity(monster); } @@ -336,9 +364,8 @@ public final class PlayerCommands { } } } - - @Command(label = "killall", - usage = "Usage: killall [playerUid] [sceneId]") + + @Command(label = "killall", usage = "Usage: killall [playerUid] [sceneId]") public static class KillAllCommand implements CommandHandler { @Override @@ -349,29 +376,30 @@ public final class PlayerCommands { .forEach(entity -> scene.killEntity(entity, 0)); CommandHandler.sendMessage(null, "Killing all monsters in scene " + scene.getId()); } - + @Override public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); return; + if (args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); + return; } try { - int playerUid = Integer.parseInt(args.get(0)); + int playerUid = Integer.parseInt(args.get(0)); int sceneId = Integer.parseInt(args.get(1)); - + GenshinPlayer player = Grasscutter.getGameServer().getPlayerByUid(playerUid); if (player == null) { - CommandHandler.sendMessage(null, "Player not found or offline."); - return; + CommandHandler.sendMessage(null, "Player not found or offline."); + return; } - + GenshinScene scene = player.getWorld().getSceneById(sceneId); if (scene == null) { - CommandHandler.sendMessage(null, "Scene not found in player world"); - return; + CommandHandler.sendMessage(null, "Scene not found in player world"); + return; } - + scene.getEntities().values().stream() .filter(entity -> entity instanceof EntityMonster) .forEach(entity -> scene.killEntity(entity, 0)); @@ -381,28 +409,29 @@ public final class PlayerCommands { } } } - - @Command(label = "resetconst", aliases = {"resetconstellation"}, - usage = "Usage: resetconst [all]", execution = Command.Execution.PLAYER) + + @Command(label = "resetconst", aliases = { + "resetconstellation" }, usage = "Usage: resetconst [all]", execution = Command.Execution.PLAYER) public static class ResetConstellationCommand implements CommandHandler { - + @Override public void execute(GenshinPlayer player, List args) { - if(args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { + if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { player.getAvatars().forEach(this::resetConstellation); player.dropMessage("Reset all avatars' constellations."); } else { - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - if(entity == null) + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + if (entity == null) return; - + GenshinAvatar avatar = entity.getAvatar(); this.resetConstellation(avatar); - player.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes."); + player.dropMessage("Constellations for " + avatar.getAvatarData().getName() + + " have been reset. Please relog to see changes."); } } - + private void resetConstellation(GenshinAvatar avatar) { avatar.getTalentIdList().clear(); avatar.setCoreProudSkillLevel(0); @@ -410,36 +439,37 @@ public final class PlayerCommands { avatar.save(); } } - - @Command(label = "godmode", - usage = "Usage: godmode", execution = Command.Execution.PLAYER) + + @Command(label = "godmode", usage = "Usage: godmode", execution = Command.Execution.PLAYER) public static class GodModeCommand implements CommandHandler { - + @Override public void execute(GenshinPlayer player, List args) { player.setGodmode(!player.inGodmode()); player.dropMessage("Godmode is now " + (player.inGodmode() ? "enabled" : "disabled") + "."); } } - - @Command(label = "sethealth", aliases = {"sethp"}, - usage = "Usage: sethealth ", execution = Command.Execution.PLAYER) + + @Command(label = "sethealth", aliases = { + "sethp" }, usage = "Usage: sethealth ", execution = Command.Execution.PLAYER) public static class SetHealthCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: sethealth "); return; + if (args.size() < 1) { + CommandHandler.sendMessage(null, "Usage: sethealth "); + return; } - + try { int health = Integer.parseInt(args.get(0)); EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - if(entity == null) + if (entity == null) return; - + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + entity.getWorld().broadcastPacket( + new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); player.dropMessage("Health set to " + health + "."); } catch (NumberFormatException ignored) { CommandHandler.sendMessage(null, "Invalid health value."); @@ -447,15 +477,16 @@ public final class PlayerCommands { } } - @Command(label = "setworldlevel", aliases = {"setworldlvl"}, - usage = "Usage: setworldlevel ", execution = Command.Execution.PLAYER) + @Command(label = "setworldlevel", aliases = { + "setworldlvl" }, usage = "Usage: setworldlevel ", execution = Command.Execution.PLAYER) public static class SetWorldLevelCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: setworldlevel "); return; + if (args.size() < 1) { + CommandHandler.sendMessage(player, "Usage: setworldlevel "); + return; } - + try { int level = Integer.parseInt(args.get(0)); @@ -469,9 +500,9 @@ public final class PlayerCommands { } } } - - @Command(label = "clearartifacts", aliases = {"clearart"}, - usage = "Usage: clearartifacts", execution = Command.Execution.PLAYER) + + @Command(label = "clearartifacts", aliases = { + "clearart" }, usage = "Usage: clearartifacts", execution = Command.Execution.PLAYER) public static class ClearArtifactsCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { @@ -484,15 +515,16 @@ public final class PlayerCommands { } } - @Command(label = "changescene", aliases = {"scene"}, - usage = "Usage: changescene ", execution = Command.Execution.PLAYER) + @Command(label = "changescene", aliases = { + "scene" }, usage = "Usage: changescene ", execution = Command.Execution.PLAYER) public static class ChangeSceneCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: changescene "); return; + if (args.size() < 1) { + CommandHandler.sendMessage(player, "Usage: changescene "); + return; } - + try { int sceneId = Integer.parseInt(args.get(0)); boolean result = player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); @@ -501,8 +533,18 @@ public final class PlayerCommands { CommandHandler.sendMessage(null, "Scene does not exist or you are already in it"); } } catch (Exception e) { - CommandHandler.sendMessage(player, "Usage: changescene "); return; + CommandHandler.sendMessage(player, "Usage: changescene "); + return; } } } + + @Command(label = "restart", usage = "Usage: restart - Restarts the current session", execution = Command.Execution.PLAYER) + public static class RestartCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer player, List args) { + player.getSession().close(); + } + } } From d603681355723a85cb8db23b73d2996ff551a880 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Tue, 19 Apr 2022 19:45:39 +0200 Subject: [PATCH 03/91] Try PlayerTokenRsp instead of LoginRsp --- src/main/java/emu/grasscutter/commands/PlayerCommands.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 57976381a..0a84b5424 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -20,6 +20,7 @@ import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketGetPlayerTokenRsp; import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; import emu.grasscutter.server.packet.send.PacketPlayerLoginRsp; import emu.grasscutter.utils.Position; @@ -544,7 +545,8 @@ public final class PlayerCommands { @Override public void execute(GenshinPlayer player, List args) { - player.getSession().close(); + // player.getSession().close(); + player.getSession().send(new PacketGetPlayerTokenRsp(player.getSession(), true)); } } } From 167b25213ec71d2304dc20b6e4d52c0926eb6b5b Mon Sep 17 00:00:00 2001 From: junu128 <93566768+junu128@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:50:05 +0800 Subject: [PATCH 04/91] Add files via upload --- add-to-wiki/resources-fun.md | 77 ++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 add-to-wiki/resources-fun.md diff --git a/add-to-wiki/resources-fun.md b/add-to-wiki/resources-fun.md new file mode 100644 index 000000000..7fc3653f6 --- /dev/null +++ b/add-to-wiki/resources-fun.md @@ -0,0 +1,77 @@ +# Fun +## Prerequisites +- [MongoDBCompass](https://www.mongodb.com/try/download/compass) + +*** + +### Colored nickname and signature :peacock: ([written by](https://github.com/actuallyeunha)) +Unity supports colored text by default (See [Unity Manual](https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/StyledText.html) for more info) + +By replacing them with `text` or `text` you can change most, if not all, strings' colors. + + +1. Open MongoDBCompass and connect to your db +2. Go to `grasscutter/players` +3. Make your changes. *Change it inside `playerProfile` too!*
+ e.g `nickname: "na.na"`;
+ `signature: "Running on Grasscutter!"` +4. Update the document +5. Relog to see changes + +Enjoy your colorful name :sparkles: + + +*** +## Avatar/Character +### Changing Level/Ascension/Talents +> Make sure you have created an account with the avatars/characters. +1. Open **MongoDBCompass** +2. Connect to the host (default URI is `mongodb://localhost:27017`) +3. Navigate to Databases > `grasscutter` > `avatars` +4. (Optional) Set **View** to `JSON View` +5. In the **Filter** field, type `{ avatarId: X }` where `X` is the Avatar ID that you are trying to modify. **Avatar IDs are 8-digits.** +6. Click the **Find** next to the field. +7. After the documents have been filtered, edit the document + 1. To change character **level**, change the value next to `"level"` + 2. To change character **ascension**, change the value next to `"promoteLevel"` + Check [Wiki](https://genshin-impact.fandom.com/wiki/Characters#:~:text=one%20Acquaint%20Fate.-,Ascension%20Phase,-Max%20Char.%20Level) for ascension values + 3. To change **talents**, expand `"proudSkillList"` + 1. To unlock the avatar's **1st Ascension Passive**, add XX2101 inside `"proudSkillList"`, where **XX is the last 2 digits of the Avatar ID**. **Make sure to add a `,` for each line.** + 2. To unlock the avatar's **4th Ascension Passive**, do the same as before, but replace `2101` with `2201`. +8. After editing, you can now **Replace**. + +### Example +A document in JSON view that has the Avatar ID 10000058, level 90, ascension phase 6, all talents unlocked. + + + + +### Notes +- On the last line of `"proudSkillList"`, a `,` is no longer needed. +- After replacing the document, make sure to restart the server for changes to take effect. + + +## Weapons +> Make sure you have created an account with the weapons. +1. Open **MongoDBCompass** +2. Connect to the host (default URI is `mongodb://localhost:27017`) +3. Navigate to Databases > `grasscutter` > `items` +4. (Optional) Set **View** to `JSON View` +5. In the **Filter** field, type `{ itemId: Y }` where `Y` is the Item/Weapon ID that you are trying to modify. **Weapon IDs are 5 digits.** +6. Click the **Find** next to the field. +7. After the documents have been filtered, edit the document + 1. To change weapon **level**, change the value next to `"level"` + 2. To change weapon **ascension**, change the value next to `"promoteLevel"` + Check [Wiki](https://genshin-impact.fandom.com/wiki/Weapons#:~:text=reaching%202nd%20Ascension.-,Ascension%20Phase,-Max%20Weapon%20Level) for ascension values + 3. To change **refinement**, change the value next to `"refinement"` + * Refinement Rank 1 = 0 + * Refinement Rank 2 = 1 + * Refinement Rank 3 = 2 + * Refinement Rank 4 = 3 + * Refinement Rank 5 = 4 +8. After editing, you can now **Replace**. + +### Example +A document in JSON view that has the Weapon ID 12503, level 90, ascension phase 6, refinement rank 5. + + \ No newline at end of file From a35f93a7f4caa0867cafa6b5adce8344187e16db Mon Sep 17 00:00:00 2001 From: junu128 <93566768+junu128@users.noreply.github.com> Date: Wed, 20 Apr 2022 11:12:16 +0800 Subject: [PATCH 05/91] Update resources-fun.md --- add-to-wiki/resources-fun.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/add-to-wiki/resources-fun.md b/add-to-wiki/resources-fun.md index 7fc3653f6..57808f841 100644 --- a/add-to-wiki/resources-fun.md +++ b/add-to-wiki/resources-fun.md @@ -22,6 +22,7 @@ Enjoy your colorful name :sparkles: *** +This guide is recommended if you have the character/weapon. If you want to get a character at Level Z, then use !givechar [level] ## Avatar/Character ### Changing Level/Ascension/Talents > Make sure you have created an account with the avatars/characters. @@ -74,4 +75,4 @@ A document in JSON view that has the Avatar ID 10000058, level 90, ascension pha ### Example A document in JSON view that has the Weapon ID 12503, level 90, ascension phase 6, refinement rank 5. - \ No newline at end of file + From e28509c350ff47bd3b199d8fb21d6bf742f7f654 Mon Sep 17 00:00:00 2001 From: junu128 <93566768+junu128@users.noreply.github.com> Date: Wed, 20 Apr 2022 11:12:56 +0800 Subject: [PATCH 06/91] Update resources-fun.md --- add-to-wiki/resources-fun.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add-to-wiki/resources-fun.md b/add-to-wiki/resources-fun.md index 57808f841..86c0e898c 100644 --- a/add-to-wiki/resources-fun.md +++ b/add-to-wiki/resources-fun.md @@ -22,8 +22,8 @@ Enjoy your colorful name :sparkles: *** -This guide is recommended if you have the character/weapon. If you want to get a character at Level Z, then use !givechar [level] ## Avatar/Character +This guide is recommended if you have the character/weapon. If you want to get a character at Level Z, then use !givechar [level] ### Changing Level/Ascension/Talents > Make sure you have created an account with the avatars/characters. 1. Open **MongoDBCompass** From 6c938e6ca52eb0eb8c7934a3860184a49c7f3d61 Mon Sep 17 00:00:00 2001 From: junu128 <93566768+junu128@users.noreply.github.com> Date: Wed, 20 Apr 2022 11:50:56 +0800 Subject: [PATCH 07/91] Update resources-fun.md --- add-to-wiki/resources-fun.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add-to-wiki/resources-fun.md b/add-to-wiki/resources-fun.md index 86c0e898c..6b1afea25 100644 --- a/add-to-wiki/resources-fun.md +++ b/add-to-wiki/resources-fun.md @@ -23,7 +23,7 @@ Enjoy your colorful name :sparkles: *** ## Avatar/Character -This guide is recommended if you have the character/weapon. If you want to get a character at Level Z, then use !givechar [level] +This guide is recommended if you have the character/weapon. If you want to get a character at Level Z, then use !givechar [avatarID] [level] ### Changing Level/Ascension/Talents > Make sure you have created an account with the avatars/characters. 1. Open **MongoDBCompass** From 716b6df2ecc5cba5c5d0b35cbde9f0b1873598a7 Mon Sep 17 00:00:00 2001 From: w4123 <1840686745@qq.com> Date: Wed, 20 Apr 2022 12:25:38 +0800 Subject: [PATCH 08/91] Implement stub NpcTalk packets --- .../server/packet/recv/HandlerNpcTalkReq.java | 21 +++++++++++++++++++ .../server/packet/send/PacketNpcTalkRsp.java | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketNpcTalkRsp.java diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java new file mode 100644 index 000000000..588e1c4e1 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.NpcTalkReqOuterClass.NpcTalkReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketNpcTalkRsp; + +@Opcodes(PacketOpcodes.NpcTalkReq) +public class HandlerNpcTalkReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + NpcTalkReq req = NpcTalkReq.parseFrom(payload); + + session.send(new PacketNpcTalkRsp(req.getNpcEntityId(), req.getTalkId(), req.getEntityId())); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketNpcTalkRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketNpcTalkRsp.java new file mode 100644 index 000000000..b447a19eb --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketNpcTalkRsp.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.NpcTalkRspOuterClass.NpcTalkRsp; + +public class PacketNpcTalkRsp extends GenshinPacket { + public PacketNpcTalkRsp(int npcEntityId, int curTalkId, int entityId) { + super(PacketOpcodes.NpcTalkRsp); + + NpcTalkRsp p = NpcTalkRsp.newBuilder() + .setNpcEntityId(npcEntityId) + .setCurTalkId(curTalkId) + .setEntityId(entityId) + .build(); + + this.setData(p); + } +} From ad1e9400e390ed919ccb86b3b2f2f91008c1cdd0 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Wed, 20 Apr 2022 08:52:47 +0200 Subject: [PATCH 09/91] It won't get better than this for now.. --- src/main/java/emu/grasscutter/commands/PlayerCommands.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 0a84b5424..2c0827b82 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -545,8 +545,7 @@ public final class PlayerCommands { @Override public void execute(GenshinPlayer player, List args) { - // player.getSession().close(); - player.getSession().send(new PacketGetPlayerTokenRsp(player.getSession(), true)); + player.getSession().close(); } } } From 2632ae373c3250239f30a546614cec74cada67cc Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Wed, 20 Apr 2022 09:00:50 +0200 Subject: [PATCH 10/91] Fix merge conflict --- .../grasscutter/commands/PlayerCommands.java | 85 +++++++++++++------ 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 2c0827b82..ea2cbfbd6 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -32,8 +32,8 @@ import java.util.List; * A container for player-related commands. */ public final class PlayerCommands { - @Command(label = "give", aliases = { "g", "item", - "giveitem" }, usage = "Usage: give [player] [amount]") + @Command(label = "give", aliases = {"g", "item", "giveitem"}, + usage = "give [player] [amount]", description = "Gives an item to you or the specified player", permission = "player.give") public static class GiveCommand implements CommandHandler { @Override @@ -158,9 +158,9 @@ public final class PlayerCommands { } } } - - @Command(label = "drop", aliases = { "d", - "dropitem" }, usage = "Usage: drop [amount]", execution = Command.Execution.PLAYER) + @Command(label = "drop", aliases = {"d", "dropitem"}, + usage = "drop [amount]", + execution = Command.Execution.PLAYER, description = "Drops an item near you", permission = "server.drop") public static class DropCommand implements CommandHandler { @Override @@ -201,8 +201,8 @@ public final class PlayerCommands { } } - @Command(label = "givechar", aliases = { - "givec" }, usage = "Usage: givechar [level|avatarId] [level]") + @Command(label = "givechar", aliases = { "givec" }, usage = "givechar [level]", + description = "Gives the player a specified character", permission = "player.givechar") public static class GiveCharCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { @@ -328,7 +328,8 @@ public final class PlayerCommands { } } - @Command(label = "spawn", execution = Command.Execution.PLAYER, usage = "Usage: spawn [level] [amount]") + @Command(label = "spawn", execution = Command.Execution.PLAYER, + usage = "spawn [level] [amount]", description = "Spawns an entity near you", permission = "server.spawn") public static class SpawnCommand implements CommandHandler { @Override @@ -365,8 +366,9 @@ public final class PlayerCommands { } } } - - @Command(label = "killall", usage = "Usage: killall [playerUid] [sceneId]") + + @Command(label = "killall", + usage = "killall [playerUid] [sceneId]", description = "Kill all entities", permission = "server.killall") public static class KillAllCommand implements CommandHandler { @Override @@ -411,8 +413,9 @@ public final class PlayerCommands { } } - @Command(label = "resetconst", aliases = { - "resetconstellation" }, usage = "Usage: resetconst [all]", execution = Command.Execution.PLAYER) + @Command(label = "resetconst", aliases = {"resetconstellation"}, + usage = "resetconst [all]", execution = Command.Execution.PLAYER, permission = "player.resetconstellation", + description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.") public static class ResetConstellationCommand implements CommandHandler { @Override @@ -440,8 +443,9 @@ public final class PlayerCommands { avatar.save(); } } - - @Command(label = "godmode", usage = "Usage: godmode", execution = Command.Execution.PLAYER) + + @Command(label = "godmode", + usage = "godmode", execution = Command.Execution.PLAYER, description = "Prevents you from taking damage", permission = "player.godmode") public static class GodModeCommand implements CommandHandler { @Override @@ -451,8 +455,9 @@ public final class PlayerCommands { } } - @Command(label = "sethealth", aliases = { - "sethp" }, usage = "Usage: sethealth ", execution = Command.Execution.PLAYER) + @Command(label = "sethealth", aliases = {"sethp"}, + usage = "sethealth ", execution = Command.Execution.PLAYER, description = "Sets your health to the specified value", + permission = "player.sethealth") public static class SetHealthCommand implements CommandHandler { @Override @@ -478,8 +483,9 @@ public final class PlayerCommands { } } - @Command(label = "setworldlevel", aliases = { - "setworldlvl" }, usage = "Usage: setworldlevel ", execution = Command.Execution.PLAYER) + @Command(label = "setworldlevel", aliases = {"setworldlvl"}, usage = "setworldlevel ", + description = "Sets your world level (Relog to see proper effects)", permission = "player.setworldlevel", + execution = Command.Execution.PLAYER) public static class SetWorldLevelCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { @@ -501,9 +507,10 @@ public final class PlayerCommands { } } } - - @Command(label = "clearartifacts", aliases = { - "clearart" }, usage = "Usage: clearartifacts", execution = Command.Execution.PLAYER) + + @Command(label = "clearartifacts", aliases = {"clearart"}, + usage = "clearartifacts", execution = Command.Execution.PLAYER, permission = "player.clearartifacts", + description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory") public static class ClearArtifactsCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { @@ -516,8 +523,8 @@ public final class PlayerCommands { } } - @Command(label = "changescene", aliases = { - "scene" }, usage = "Usage: changescene ", execution = Command.Execution.PLAYER) + @Command(label = "changescene", aliases = {"scene"}, + usage = "changescene ", description = "Changes your scene", permission = "player.changescene", execution = Command.Execution.PLAYER) public static class ChangeSceneCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { @@ -540,12 +547,38 @@ public final class PlayerCommands { } } - @Command(label = "restart", usage = "Usage: restart - Restarts the current session", execution = Command.Execution.PLAYER) - public static class RestartCommand implements CommandHandler { + @Command(label = "sendservermessage", aliases = {"sendservmsg"}, + usage = "sendservermessage ", description = "Sends a message to a player as the server", + execution = Command.Execution.PLAYER, permission = "server.sendmessage") + public static class SendServerMessageCommand implements CommandHandler { + @Override + public void execute(GenshinPlayer player, List args) { + if(args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: sendmessage "); return; + } + try { + int target = Integer.parseInt(args.get(0)); + String message = String.join(" ", args.subList(1, args.size())); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if(targetPlayer == null) { + CommandHandler.sendMessage(null, "Player not found."); return; + } + + targetPlayer.dropMessage(message); + CommandHandler.sendMessage(null, "Message sent."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid player ID."); + } + } + } + + @Command(label = "restart", usage = "Usage: restart", description = "Restarts the current session", execution = Command.Execution.PLAYER, permission = "player.restart") + public static class RestartCommand implements CommandHandler { @Override public void execute(GenshinPlayer player, List args) { player.getSession().close(); } } -} +} \ No newline at end of file From d59799ce55b777806acc3f73f8ea4dcc84dc7201 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 16:05:53 +0800 Subject: [PATCH 11/91] Update gradle + Work gacha reload (gs broken atm) --- build.gradle | 24 ++++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/java/emu/grasscutter/Config.java | 3 +- .../grasscutter/game/gacha/GachaManager.java | 46 ++++++++++++++++++- .../grasscutter/server/game/GameServer.java | 10 ++++ .../server/game/GameServerTickEvent.java | 5 ++ 6 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/game/GameServerTickEvent.java diff --git a/build.gradle b/build.gradle index c7c9eae79..97477456a 100644 --- a/build.gradle +++ b/build.gradle @@ -23,19 +23,21 @@ repositories { } dependencies { - compile fileTree(dir: 'lib', include: '*.jar') + implementation fileTree(dir: 'lib', include: ['*.jar']) - compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32' - compile group: 'ch.qos.logback', name: 'logback-core', version: '1.2.6' - compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.6' - compile group: 'io.netty', name: 'netty-all', version: '4.1.69.Final' + implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32' + implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.6' + implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.6' + implementation group: 'io.netty', name: 'netty-all', version: '4.1.69.Final' - compile group: 'com.google.code.gson', name: 'gson', version: '2.8.8' - compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.1' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.8' + implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.1' - compile group: 'org.reflections', name: 'reflections', version: '0.9.12' + implementation group: 'org.reflections', name: 'reflections', version: '0.9.12' - compile group: 'dev.morphia.morphia', name: 'core', version: '1.6.1' + implementation group: 'dev.morphia.morphia', name: 'core', version: '1.6.1' + + implementation group: 'org.greenrobot', name: 'eventbus-java', version: '3.3.1' } application { @@ -51,9 +53,11 @@ jar { jar.baseName = 'grasscutter' from { - configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } + duplicatesStrategy = DuplicatesStrategy.INCLUDE + from('src/main/java') { include '*.xml' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f04d6a20a..aa991fcea 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index 983524223..510d7c822 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -24,7 +24,7 @@ public final class Config { public String DUMPS_FOLDER = "./dumps/"; public String KEY_FOLDER = "./keys/"; public boolean LOG_PACKETS = false; - + public GameRates Game = new GameRates(); public ServerOptions ServerOptions = new ServerOptions(); @@ -51,6 +51,7 @@ public final class Config { public int MaxAvatarsInTeam = 4; public int MaxAvatarsInTeamMultiplayer = 4; public int MaxEntityLimit = 1000; // Max entity limit per world. // TODO: Enforce later. + public boolean WatchGacha = false; public int[] WelcomeEmotes = {2007, 1002, 4010}; public String WelcomeMotd = "Welcome to Grasscutter emu"; } diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index 692e09264..390fb5b95 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -1,6 +1,8 @@ package emu.grasscutter.game.gacha; +import java.io.File; import java.io.FileReader; +import java.nio.file.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -21,17 +23,23 @@ import emu.grasscutter.net.proto.GachaTransferItemOuterClass.GachaTransferItem; import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp; import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam; import emu.grasscutter.server.game.GameServer; +import emu.grasscutter.server.game.GameServerTickEvent; import emu.grasscutter.server.packet.send.PacketDoGachaRsp; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import org.greenrobot.eventbus.Subscribe; + +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; public class GachaManager { private final GameServer server; private final Int2ObjectMap gachaBanners; private GetGachaInfoRsp cachedProto; - + WatchService watchService; + WatchKey watchKey; + private int[] yellowAvatars = new int[] {1003, 1016, 1042, 1035, 1041}; private int[] yellowWeapons = new int[] {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502}; private int[] purpleAvatars = new int[] {1006, 1014, 1015, 1020, 1021, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064}; @@ -40,11 +48,12 @@ public class GachaManager { private static int starglitterId = 221; private static int stardustId = 222; - + public GachaManager(GameServer server) { this.server = server; this.gachaBanners = new Int2ObjectOpenHashMap<>(); this.load(); + this.startWatcher(server); } public GameServer getServer() { @@ -266,6 +275,39 @@ public class GachaManager { // Packets player.sendPacket(new PacketDoGachaRsp(banner, list)); } + + private synchronized void startWatcher(GameServer server) { + if(this.watchService == null) { + try { + this.watchService = FileSystems.getDefault().newWatchService(); + Path path = new File(Grasscutter.getConfig().DATA_FOLDER).toPath(); + path.register(watchService, ENTRY_MODIFY); + watchKey = watchService.take(); + + server.OnGameServerTick.register(this); + } catch (Exception e) { + Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload"); + e.printStackTrace(); + } + } else { + Grasscutter.getLogger().error("Cannot reinitialise watcher "); + } + } + + @Subscribe + public synchronized void watchBannerJson(GameServerTickEvent tickEvent) { + try { + for (WatchEvent event : watchKey.pollEvents()) { + final Path changed = (Path) event.context(); + if (changed.endsWith("Banners.json")) { + Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config"); + this.load(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } private synchronized GetGachaInfoRsp createProto() { GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345); diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index b42ced55c..7304abc9d 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -3,6 +3,7 @@ package emu.grasscutter.server.game; import java.net.InetSocketAddress; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; @@ -19,6 +20,8 @@ import emu.grasscutter.game.shop.ShopManager; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.netty.MihoyoKcpServer; +import jdk.internal.event.Event; +import org.greenrobot.eventbus.EventBus; public final class GameServer extends MihoyoKcpServer { private final InetSocketAddress address; @@ -33,9 +36,14 @@ public final class GameServer extends MihoyoKcpServer { private final MultiplayerManager multiplayerManager; private final DungeonManager dungeonManager; private final CommandMap commandMap; + + public EventBus OnGameServerTick; + public EventBus OnGameServerStop; // TODO public GameServer(InetSocketAddress address) { super(address); + + OnGameServerTick = EventBus.builder().throwSubscriberException(true).build(); this.setServerInitializer(new GameServerInitializer(this)); this.address = address; @@ -155,6 +163,8 @@ public final class GameServer extends MihoyoKcpServer { for (GenshinPlayer player : this.getPlayers().values()) { player.onTick(); } + + OnGameServerTick.post(new GameServerTickEvent()); } @Override diff --git a/src/main/java/emu/grasscutter/server/game/GameServerTickEvent.java b/src/main/java/emu/grasscutter/server/game/GameServerTickEvent.java new file mode 100644 index 000000000..f9069d2d9 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/game/GameServerTickEvent.java @@ -0,0 +1,5 @@ +package emu.grasscutter.server.game; + +public class GameServerTickEvent { + // Placeholder class for now, probably will get used later +} From 28a070f19abd1bc71127e0588e9446297fc4a72d Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 16:07:18 +0800 Subject: [PATCH 12/91] Forgot config check --- .../grasscutter/game/gacha/GachaManager.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index 390fb5b95..d52dff8bf 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -296,16 +296,18 @@ public class GachaManager { @Subscribe public synchronized void watchBannerJson(GameServerTickEvent tickEvent) { - try { - for (WatchEvent event : watchKey.pollEvents()) { - final Path changed = (Path) event.context(); - if (changed.endsWith("Banners.json")) { - Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config"); - this.load(); + if(Grasscutter.getConfig().getServerOptions().WatchGacha) { + try { + for (WatchEvent event : watchKey.pollEvents()) { + final Path changed = (Path) event.context(); + if (changed.endsWith("Banners.json")) { + Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config"); + this.load(); + } } + } catch (Exception e) { + e.printStackTrace(); } - } catch (Exception e) { - e.printStackTrace(); } } From 26e1341ce9ba53f23ef837fda6f019ab8a16942d Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 17:14:07 +0800 Subject: [PATCH 13/91] Out of my madness and suffering I have achieved the impossible. I almost lost all hope and was about to end it all. However, it worked out in the end and now we have gacha reloading. --- .../grasscutter/game/gacha/GachaManager.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index d52dff8bf..eb1cba379 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -10,6 +10,7 @@ import java.util.concurrent.ThreadLocalRandom; import com.google.gson.reflect.TypeToken; +import com.sun.nio.file.SensitivityWatchEventModifier; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.def.ItemData; @@ -31,14 +32,11 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import org.greenrobot.eventbus.Subscribe; -import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; - public class GachaManager { private final GameServer server; private final Int2ObjectMap gachaBanners; private GetGachaInfoRsp cachedProto; WatchService watchService; - WatchKey watchKey; private int[] yellowAvatars = new int[] {1003, 1016, 1042, 1035, 1041}; private int[] yellowWeapons = new int[] {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502}; @@ -74,9 +72,16 @@ public class GachaManager { public synchronized void load() { try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "Banners.json")) { + getGachaBanners().clear(); List banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType()); - for (GachaBanner banner : banners) { - getGachaBanners().put(banner.getGachaType(), banner); + if(banners.size() > 0) { + for (GachaBanner banner : banners) { + getGachaBanners().put(banner.getGachaType(), banner); + } + Grasscutter.getLogger().info("Banners successfully loaded."); + this.cachedProto = createProto(); + } else { + Grasscutter.getLogger().error("Unable to load banners. Banners size is 0."); } } catch (Exception e) { // TODO Auto-generated catch block @@ -281,8 +286,7 @@ public class GachaManager { try { this.watchService = FileSystems.getDefault().newWatchService(); Path path = new File(Grasscutter.getConfig().DATA_FOLDER).toPath(); - path.register(watchService, ENTRY_MODIFY); - watchKey = watchService.take(); + path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH); server.OnGameServerTick.register(this); } catch (Exception e) { @@ -298,6 +302,8 @@ public class GachaManager { public synchronized void watchBannerJson(GameServerTickEvent tickEvent) { if(Grasscutter.getConfig().getServerOptions().WatchGacha) { try { + WatchKey watchKey = watchService.take(); + for (WatchEvent event : watchKey.pollEvents()) { final Path changed = (Path) event.context(); if (changed.endsWith("Banners.json")) { @@ -305,6 +311,12 @@ public class GachaManager { this.load(); } } + + boolean valid = watchKey.reset(); + if (!valid) { + Grasscutter.getLogger().error("Unable to reset Gacha Manager Watch Key. Auto-reload of banners.json will no longer work."); + return; + } } catch (Exception e) { e.printStackTrace(); } From 500580f368b7308e837558c6af86a7ab7058aa85 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Wed, 20 Apr 2022 17:16:21 +0800 Subject: [PATCH 14/91] Add /weather Command --- .../grasscutter/commands/PlayerCommands.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 779e91ef9..656f85a20 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -16,10 +16,12 @@ import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.inventory.GenshinItem; import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.inventory.ItemType; +import emu.grasscutter.game.props.ClimateType; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify; import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; import emu.grasscutter.utils.Position; @@ -536,4 +538,29 @@ public final class PlayerCommands { } } } + + @Command(label = "weather", aliases = {"weather", "w"}, + usage = "weather ", description = "Changes the weather.", + execution = Command.Execution.PLAYER, permission = "player.weather" + ) + public static class ChangeWeatherCommand implements CommandHandler { + @Override + public void execute(GenshinPlayer player, List args) { + if (args.size() < 1) { + CommandHandler.sendMessage(player, "Usage: weather "); + return; + } + + try { + int weatherId = Integer.parseInt(args.get(0)); + + ClimateType climate = ClimateType.getTypeByValue(weatherId); + + player.getScene().setClimate(climate); + player.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(player)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(player, "Invalid weather ID."); + } + } + } } From df264bacd9c9d950e36a6eae3684ae8a31437d1a Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 02:16:39 -0700 Subject: [PATCH 15/91] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e39d4e70d..5041991e8 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ There is a dummy user named "Server" in every player's friends list that you can `!clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including yellow rarity ones** from your inventory ### Bonus -When you want to teleport to somewhere, use the ingame marking function on Map, then named it `pos`, click Confirm. You will see your character falling from a very high destination, exact location that you marked. +When you want to teleport to somewhere, use the ingame marking function on Map, click Confirm. You will see your character falling from a very high destination, exact location that you marked. # Quick Troubleshooting * If compiling wasnt successful, please check your JDK installation (must be JDK 8 and validated JDK's bin PATH variable) From 1c2560736f533e8cffb025d7767236f8ba6d325f Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 17:17:59 +0800 Subject: [PATCH 16/91] /reload now reloads the gacha as well --- src/main/java/emu/grasscutter/commands/ServerCommands.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/emu/grasscutter/commands/ServerCommands.java b/src/main/java/emu/grasscutter/commands/ServerCommands.java index edeac6ca3..d721edd07 100644 --- a/src/main/java/emu/grasscutter/commands/ServerCommands.java +++ b/src/main/java/emu/grasscutter/commands/ServerCommands.java @@ -18,6 +18,7 @@ public final class ServerCommands { public void execute(List args) { Grasscutter.getLogger().info("Reloading config."); Grasscutter.loadConfig(); + Grasscutter.getGameServer().getGachaManager().load(); Grasscutter.getDispatchServer().loadQueries(); Grasscutter.getLogger().info("Reload complete."); } From b7f5cc9748a205409fd57faf3ab700ce15b94764 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 17:31:41 +0800 Subject: [PATCH 17/91] Added OnGameServerStop EventBus There is a temporary warning message telling users to ignore the 'No subscribers registered' error. This can be removed once things actually subscribe to it --- .../java/emu/grasscutter/server/game/GameServer.java | 10 ++++++++-- .../grasscutter/server/game/GameServerStopEvent.java | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/game/GameServerStopEvent.java diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 7304abc9d..155e84979 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -38,13 +38,14 @@ public final class GameServer extends MihoyoKcpServer { private final CommandMap commandMap; public EventBus OnGameServerTick; - public EventBus OnGameServerStop; // TODO + public EventBus OnGameServerStop; public GameServer(InetSocketAddress address) { super(address); OnGameServerTick = EventBus.builder().throwSubscriberException(true).build(); - + OnGameServerStop = EventBus.builder().throwSubscriberException(true).build(); + this.setServerInitializer(new GameServerInitializer(this)); this.address = address; this.packetHandler = new GameServerPacketHandler(PacketHandler.class); @@ -173,6 +174,11 @@ public final class GameServer extends MihoyoKcpServer { } public void onServerShutdown() { + OnGameServerStop.post(new GameServerStopEvent()); + Grasscutter.getLogger().info("Ignore the 'No subscribers registered' error"); + // TODO: Remove the log once things actually listen to OnGameServerStop. + // I just added it there to prevent people from flooding #support with this error + // Kick and save all players List list = new ArrayList<>(this.getPlayers().size()); list.addAll(this.getPlayers().values()); diff --git a/src/main/java/emu/grasscutter/server/game/GameServerStopEvent.java b/src/main/java/emu/grasscutter/server/game/GameServerStopEvent.java new file mode 100644 index 000000000..7a7ef40d7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/game/GameServerStopEvent.java @@ -0,0 +1,5 @@ +package emu.grasscutter.server.game; + +public class GameServerStopEvent { + // Placeholder class for now, probably will get used later +} From e8601de5d565fbaa4cc622c94918797a89448521 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Wed, 20 Apr 2022 17:44:44 +0800 Subject: [PATCH 18/91] Added OnGameServerStartFinish EventBus Removed those temporary warning messages as I figured out how to disable them --- .../emu/grasscutter/server/game/GameServer.java | 13 ++++++------- .../server/game/GameServerStartFinishEvent.java | 5 +++++ 2 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/game/GameServerStartFinishEvent.java diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 155e84979..282589a4b 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -3,7 +3,6 @@ package emu.grasscutter.server.game; import java.net.InetSocketAddress; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; @@ -20,7 +19,6 @@ import emu.grasscutter.game.shop.ShopManager; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.netty.MihoyoKcpServer; -import jdk.internal.event.Event; import org.greenrobot.eventbus.EventBus; public final class GameServer extends MihoyoKcpServer { @@ -37,14 +35,16 @@ public final class GameServer extends MihoyoKcpServer { private final DungeonManager dungeonManager; private final CommandMap commandMap; + public EventBus OnGameServerStartFinish; public EventBus OnGameServerTick; public EventBus OnGameServerStop; public GameServer(InetSocketAddress address) { super(address); - OnGameServerTick = EventBus.builder().throwSubscriberException(true).build(); - OnGameServerStop = EventBus.builder().throwSubscriberException(true).build(); + OnGameServerStartFinish = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build(); + OnGameServerTick = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build(); + OnGameServerStop = EventBus.builder().throwSubscriberException(true).logNoSubscriberMessages(false).build(); this.setServerInitializer(new GameServerInitializer(this)); this.address = address; @@ -171,13 +171,12 @@ public final class GameServer extends MihoyoKcpServer { @Override public void onStartFinish() { Grasscutter.getLogger().info("Game Server started on port " + address.getPort()); + + OnGameServerStartFinish.post(new GameServerStartFinishEvent()); } public void onServerShutdown() { OnGameServerStop.post(new GameServerStopEvent()); - Grasscutter.getLogger().info("Ignore the 'No subscribers registered' error"); - // TODO: Remove the log once things actually listen to OnGameServerStop. - // I just added it there to prevent people from flooding #support with this error // Kick and save all players List list = new ArrayList<>(this.getPlayers().size()); diff --git a/src/main/java/emu/grasscutter/server/game/GameServerStartFinishEvent.java b/src/main/java/emu/grasscutter/server/game/GameServerStartFinishEvent.java new file mode 100644 index 000000000..5e960ad1d --- /dev/null +++ b/src/main/java/emu/grasscutter/server/game/GameServerStartFinishEvent.java @@ -0,0 +1,5 @@ +package emu.grasscutter.server.game; + +public class GameServerStartFinishEvent { + // Placeholder class for now, probably will get used later +} From fda205015bac23bd39f4bf4218cd89a2738b0fa0 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 03:28:54 -0700 Subject: [PATCH 19/91] Fix account not found issue when logging in --- .../server/dispatch/DispatchServer.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index dab6ba0a4..f13b9dc2f 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -226,18 +226,22 @@ public final class DispatchServer { Account account = DatabaseHelper.getAccountByName(requestData.account); // Check if account exists, else create a new one. - if (account == null && Grasscutter.getConfig().ServerOptions.AutomaticallyCreateAccounts) { - // This account has been created AUTOMATICALLY. There will be no permissions added. - account = DatabaseHelper.createAccountWithId(requestData.account, 0); - - responseData.message = "OK"; - responseData.data.account.uid = account.getId(); - responseData.data.account.token = account.generateSessionKey(); - responseData.data.account.email = account.getEmail(); - } else if (!Grasscutter.getConfig().ServerOptions.AutomaticallyCreateAccounts) { - responseData.retcode = -201; - responseData.message = "Username not found."; + if (account == null) { + // Account doesnt exist, so we can either auto create it if the config value is set + if (Grasscutter.getConfig().ServerOptions.AutomaticallyCreateAccounts) { + // This account has been created AUTOMATICALLY. There will be no permissions added. + account = DatabaseHelper.createAccountWithId(requestData.account, 0); + + responseData.message = "OK"; + responseData.data.account.uid = account.getId(); + responseData.data.account.token = account.generateSessionKey(); + responseData.data.account.email = account.getEmail(); + } else { + responseData.retcode = -201; + responseData.message = "Username not found."; + } } else { + // Account was found, log the player in responseData.message = "OK"; responseData.data.account.uid = account.getId(); responseData.data.account.token = account.generateSessionKey(); From aeb335c8430c931cf4a485177ca156e4acf0ad5b Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Wed, 20 Apr 2022 17:40:26 +0700 Subject: [PATCH 20/91] Adding /pos Some people want to get the coordinate, here you go! --- .../java/emu/grasscutter/commands/PlayerCommands.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 4b7a2b8e1..7426284da 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -573,6 +573,17 @@ public final class PlayerCommands { } } } + + @Command(label = "pos", + usage = "Usage: pos", description = "Get coordinates.", + execution = Command.Execution.PLAYER) + public static class CordCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer player, List args) { + player.dropMessage(String.format("Coord: %.3f, %.3f, %.3f", player.getPos().getX(), player.getPos().getY(), player.getPos().getZ())); + } + } @Command(label = "restart", usage = "Usage: restart", description = "Restarts the current session", execution = Command.Execution.PLAYER, permission = "player.restart") public static class RestartCommand implements CommandHandler { From 5a09c22aa1987641f15c6ca728bf554b518f9eec Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 03:42:59 -0700 Subject: [PATCH 21/91] Fix character abilities not showing for others in co-op/lasting too long --- .../emu/grasscutter/game/GenshinPlayer.java | 7 +++++ .../HandlerClientAbilityInitFinishNotify.java | 26 +++++++++++++++++ .../HandlerSetEntityClientDataNotify.java | 11 ++++++- .../PacketClientAbilityInitFinishNotify.java | 29 +++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketClientAbilityInitFinishNotify.java diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 70fea04f0..1d8650e54 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -38,6 +38,7 @@ import emu.grasscutter.server.packet.send.PacketAvatarAddNotify; import emu.grasscutter.server.packet.send.PacketAvatarDataNotify; import emu.grasscutter.server.packet.send.PacketAvatarGainCostumeNotify; import emu.grasscutter.server.packet.send.PacketAvatarGainFlycloakNotify; +import emu.grasscutter.server.packet.send.PacketClientAbilityInitFinishNotify; import emu.grasscutter.server.packet.send.PacketCombatInvocationsNotify; import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; @@ -104,6 +105,7 @@ public class GenshinPlayer { @Transient private final Int2ObjectMap coopRequests; @Transient private final InvokeHandler combatInvokeHandler; @Transient private final InvokeHandler abilityInvokeHandler; + @Transient private final InvokeHandler clientAbilityInitFinishHandler; @Deprecated @SuppressWarnings({ "rawtypes", "unchecked" }) // Morphia only! public GenshinPlayer() { @@ -126,6 +128,7 @@ public class GenshinPlayer { this.coopRequests = new Int2ObjectOpenHashMap<>(); this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class); this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class); + this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class); } // On player creation @@ -389,6 +392,10 @@ public class GenshinPlayer { return this.abilityInvokeHandler; } + public InvokeHandler getClientAbilityInitFinishHandler() { + return clientAbilityInitFinishHandler; + } + public void setMpSetting(MpSettingType mpSetting) { this.mpSetting = mpSetting; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java new file mode 100644 index 000000000..cfe697b91 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; +import emu.grasscutter.net.proto.ClientAbilityInitFinishNotifyOuterClass.ClientAbilityInitFinishNotify; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; + +@Opcodes(PacketOpcodes.ClientAbilityInitFinishNotify) +public class HandlerClientAbilityInitFinishNotify extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + ClientAbilityInitFinishNotify notif = ClientAbilityInitFinishNotify.parseFrom(payload); + + for (AbilityInvokeEntry entry : notif.getInvokesList()) { + session.getPlayer().getClientAbilityInitFinishHandler().addEntry(entry.getForwardType(), entry); + } + + if (notif.getInvokesList().size() > 0) { + session.getPlayer().getClientAbilityInitFinishHandler().update(session.getPlayer()); + } + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java index accbd253d..1a60f677b 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java @@ -1,5 +1,6 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketHandler; @@ -10,7 +11,15 @@ public class HandlerSetEntityClientDataNotify extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - // Auto template + // Skip if there is no one to broadcast it too + if (session.getPlayer().getScene().getPlayerCount() <= 1) { + return; + } + + GenshinPacket packet = new GenshinPacket(PacketOpcodes.SetEntityClientDataNotify, true); + packet.setData(payload); + + session.getPlayer().getScene().broadcastPacketToOthers(session.getPlayer(), packet); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketClientAbilityInitFinishNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketClientAbilityInitFinishNotify.java new file mode 100644 index 000000000..dc676c4d1 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketClientAbilityInitFinishNotify.java @@ -0,0 +1,29 @@ +package emu.grasscutter.server.packet.send; + +import java.util.List; + +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; +import emu.grasscutter.net.proto.ClientAbilityInitFinishNotifyOuterClass.ClientAbilityInitFinishNotify; + +public class PacketClientAbilityInitFinishNotify extends GenshinPacket { + + public PacketClientAbilityInitFinishNotify(List entries) { + super(PacketOpcodes.ClientAbilityInitFinishNotify, true); + + int entityId = 0; + + if (entries.size() > 0) { + AbilityInvokeEntry entry = entries.get(0); + entityId = entry.getEntityId(); + } + + ClientAbilityInitFinishNotify proto = ClientAbilityInitFinishNotify.newBuilder() + .setEntityId(entityId) + .addAllInvokes(entries) + .build(); + + this.setData(proto); + } +} From c603093d4740338fd22b1c47550d79fd86a6da3c Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Wed, 20 Apr 2022 18:24:32 +0700 Subject: [PATCH 22/91] Replace SetHealth -> SetStats Now you can set basic stats of your current character, but I left a little notice: Input and In-game output (character stats) are not really accurate. So to get an accurate stat: For HP, ATK , DEF, Elemental Mastery (EM): input less than 10 digits. Example: /stats atk 81923 (81293 ATK) For Energy Recharge (ER), Crit Rate, Crit DMG: input less than 6 digits(0-99999) to get the game current format. Example: /stats cdmg 14236 (142.4% Crit DMG) (!) The modified stats will be set to default after you logged out. Press F to pay respect to SetHealth (2022 - 2022) --- .../grasscutter/commands/PlayerCommands.java | 125 ++++++++++++++---- 1 file changed, 102 insertions(+), 23 deletions(-) diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java index 7426284da..ec56bda7a 100644 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ b/src/main/java/emu/grasscutter/commands/PlayerCommands.java @@ -455,32 +455,111 @@ public final class PlayerCommands { } } - @Command(label = "sethealth", aliases = {"sethp"}, - usage = "sethealth ", execution = Command.Execution.PLAYER, description = "Sets your health to the specified value", - permission = "player.sethealth") - public static class SetHealthCommand implements CommandHandler { - - @Override +@Command(label = "setstats", aliases = {"stats"}, + usage = "Usage: setstats|stats ", execution = Command.Execution.PLAYER) + public static class SetStatsCommand implements CommandHandler { + @Override public void execute(GenshinPlayer player, List args) { - if (args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: sethealth "); - return; - } - - try { - int health = Integer.parseInt(args.get(0)); - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - if (entity == null) + String stat = args.get(0); + switch(stat){ + default: + CommandHandler.sendMessage(player, "Usage: setstats|stats "); return; - - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); - entity.getWorld().broadcastPacket( - new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - player.dropMessage("Health set to " + health + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid health value."); + case "hp": + try { + int health = Integer.parseInt(args.get(1)); + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + player.dropMessage("HP set to " + health + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid HP value."); + return; + } + break; + case "def": + try { + int def = Integer.parseInt(args.get(1)); + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE)); + player.dropMessage("DEF set to " + def + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid DEF value."); + return; + } + break; + case "atk": + try { + int atk = Integer.parseInt(args.get(1)); + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_ATTACK)); + player.dropMessage("ATK set to " + atk + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid ATK value."); + return; + } + break; + case "em": + try { + int em = Integer.parseInt(args.get(1)); + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_ELEMENT_MASTERY, em); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ELEMENT_MASTERY)); + player.dropMessage("Elemental Mastery set to " + em + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid EM value."); + return; + } + break; + case "er": + try { + float er = Integer.parseInt(args.get(1)); + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + float erecharge = er / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, erecharge); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY)); + float iger = erecharge * 100; + player.dropMessage("Energy recharge set to " + iger + "%."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid ER value."); + return; + } + break; + case "crate": + try { + float cr = Integer.parseInt(args.get(1)); + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + float crate = cr / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL, crate); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL)); + float igcrate = crate * 100; + player.dropMessage("Crit Rate set to " + igcrate + "%."); + + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid Crit Rate value."); + return; + } + break; + case "cdmg": + try { + float cdmg = Integer.parseInt(args.get(1)); + EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); + float cdamage = cdmg / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, cdamage); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL_HURT)); + float igcdmg = cdamage * 100; + player.dropMessage("Crit DMG set to " + igcdmg + "%"); + + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid Crit DMG value."); + return; + } + break; + } - } + } } @Command(label = "setworldlevel", aliases = {"setworldlvl"}, usage = "setworldlevel ", From 63cb0a8174ab8ce8402d04ae8cc4f276e98ef14c Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Wed, 20 Apr 2022 20:21:38 +0800 Subject: [PATCH 23/91] Rewrite commands Signed-off-by: Jaida Wu --- .../grasscutter/commands/PlayerCommands.java | 307 ---------- .../grasscutter/commands/ServerCommands.java | 171 ------ .../java/emu/grasscutter/Grasscutter.java | 2 +- .../java/emu/grasscutter/command/Command.java | 17 + .../grasscutter/command/CommandHandler.java | 25 + .../{commands => command}/CommandMap.java | 94 +-- .../grasscutter/command/commands/Account.java | 61 ++ .../command/commands/Broadcast.java | 29 + .../command/commands/ChangeScene.java | 36 ++ .../command/commands/ClearArtifacts.java | 30 + .../grasscutter/command/commands/Drop.java | 56 ++ .../grasscutter/command/commands/Give.java | 112 ++++ .../command/commands/GiveChar.java | 93 +++ .../grasscutter/command/commands/GodMode.java | 22 + .../grasscutter/command/commands/Help.java | 91 +++ .../grasscutter/command/commands/Kick.java | 31 + .../grasscutter/command/commands/KillAll.java | 64 +++ .../command/commands/Permission.java | 50 ++ .../grasscutter/command/commands/Reload.java | 21 + .../command/commands/ResetConst.java | 45 ++ .../command/commands/SendMessage.java | 37 ++ .../command/commands/SetHealth.java | 42 ++ .../command/commands/SetWorldLevel.java | 39 ++ .../grasscutter/command/commands/Spawn.java | 51 ++ .../grasscutter/command/commands/Stop.java | 23 + .../emu/grasscutter/commands/Command.java | 25 - .../grasscutter/commands/CommandHandler.java | 28 - .../grasscutter/commands/PlayerCommands.java | 539 ------------------ .../grasscutter/commands/ServerCommands.java | 333 ----------- .../game/managers/ChatManager.java | 2 +- .../grasscutter/server/game/GameServer.java | 2 +- 31 files changed, 1025 insertions(+), 1453 deletions(-) delete mode 100644 src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java delete mode 100644 src/deprecated/java/emu/grasscutter/commands/ServerCommands.java create mode 100644 src/main/java/emu/grasscutter/command/Command.java create mode 100644 src/main/java/emu/grasscutter/command/CommandHandler.java rename src/main/java/emu/grasscutter/{commands => command}/CommandMap.java (75%) create mode 100644 src/main/java/emu/grasscutter/command/commands/Account.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Broadcast.java create mode 100644 src/main/java/emu/grasscutter/command/commands/ChangeScene.java create mode 100644 src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Drop.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Give.java create mode 100644 src/main/java/emu/grasscutter/command/commands/GiveChar.java create mode 100644 src/main/java/emu/grasscutter/command/commands/GodMode.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Help.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Kick.java create mode 100644 src/main/java/emu/grasscutter/command/commands/KillAll.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Permission.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Reload.java create mode 100644 src/main/java/emu/grasscutter/command/commands/ResetConst.java create mode 100644 src/main/java/emu/grasscutter/command/commands/SendMessage.java create mode 100644 src/main/java/emu/grasscutter/command/commands/SetHealth.java create mode 100644 src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Spawn.java create mode 100644 src/main/java/emu/grasscutter/command/commands/Stop.java delete mode 100644 src/main/java/emu/grasscutter/commands/Command.java delete mode 100644 src/main/java/emu/grasscutter/commands/CommandHandler.java delete mode 100644 src/main/java/emu/grasscutter/commands/PlayerCommands.java delete mode 100644 src/main/java/emu/grasscutter/commands/ServerCommands.java diff --git a/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java b/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java deleted file mode 100644 index 2e8be354d..000000000 --- a/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java +++ /dev/null @@ -1,307 +0,0 @@ -package emu.grasscutter.commands; - -import java.lang.reflect.Modifier; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; - -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.data.def.MonsterData; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.avatar.GenshinAvatar; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.EntityItem; -import emu.grasscutter.game.entity.EntityMonster; -import emu.grasscutter.game.entity.GenshinEntity; -import emu.grasscutter.game.inventory.GenshinItem; -import emu.grasscutter.game.inventory.ItemType; -import emu.grasscutter.game.props.ActionReason; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; -import emu.grasscutter.utils.Position; - -public class PlayerCommands { - private static HashMap list = new HashMap<>(); - - static { - try { - // Look for classes - for (Class cls : PlayerCommands.class.getDeclaredClasses()) { - // Get non abstract classes - if (!Modifier.isAbstract(cls.getModifiers())) { - Command commandAnnotation = cls.getAnnotation(Command.class); - PlayerCommand command = (PlayerCommand) cls.newInstance(); - - if (commandAnnotation != null) { - command.setLevel(commandAnnotation.gmLevel()); - for (String alias : commandAnnotation.aliases()) { - if (alias.length() == 0) { - continue; - } - - String commandName = "!" + alias; - list.put(commandName, command); - commandName = "/" + alias; - list.put(commandName, command); - } - } - - String commandName = "!" + cls.getSimpleName().toLowerCase(); - list.put(commandName, command); - commandName = "/" + cls.getSimpleName().toLowerCase(); - list.put(commandName, command); - } - - } - } catch (Exception e) { - - } - } - - public static void handle(GenshinPlayer player, String msg) { - String[] split = msg.split(" "); - - // End if invalid - if (split.length == 0) { - return; - } - - // - String first = split[0].toLowerCase(); - PlayerCommand c = PlayerCommands.list.get(first); - - if (c != null) { - // Level check - if (player.getGmLevel() < c.getLevel()) { - return; - } - // Execute - int len = Math.min(first.length() + 1, msg.length()); - c.execute(player, msg.substring(len)); - } - } - - public static abstract class PlayerCommand { - // GM level required to use this command - private int level; - protected int getLevel() { return this.level; } - protected void setLevel(int minLevel) { this.level = minLevel; } - - // Main - public abstract void execute(GenshinPlayer player, String raw); - } - - // ================ Commands ================ - - @Command(aliases = {"g", "item", "additem"}, helpText = "/give [item id] [count] - Gives {count} amount of {item id}") - public static class Give extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int itemId = 0, count = 1; - - try { - itemId = Integer.parseInt(split[0]); - } catch (Exception e) { - itemId = 0; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1); - } catch (Exception e) { - count = 1; - } - - // Give - ItemData itemData = GenshinData.getItemDataMap().get(itemId); - GenshinItem item; - - if (itemData == null) { - player.dropMessage("Error: Item data not found"); - return; - } - - if (itemData.isEquip()) { - List items = new LinkedList<>(); - for (int i = 0; i < count; i++) { - item = new GenshinItem(itemData); - items.add(item); - } - player.getInventory().addItems(items); - player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); - } else { - item = new GenshinItem(itemData, count); - player.getInventory().addItem(item); - player.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop)); - } - } - } - - @Command(aliases = {"d"}, helpText = "/drop [item id] [count] - Drops {count} amount of {item id}") - public static class Drop extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int itemId = 0, count = 1; - - try { - itemId = Integer.parseInt(split[0]); - } catch (Exception e) { - itemId = 0; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1); - } catch (Exception e) { - count = 1; - } - - // Give - ItemData itemData = GenshinData.getItemDataMap().get(itemId); - - if (itemData == null) { - player.dropMessage("Error: Item data not found"); - return; - } - - if (itemData.isEquip()) { - float range = (5f + (.1f * count)); - for (int i = 0; i < count; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityItem entity = new EntityItem(player.getWorld(), player, itemData, pos, 1); - player.getWorld().addEntity(entity); - } - } else { - EntityItem entity = new EntityItem(player.getWorld(), player, itemData, player.getPos().clone().addY(3f), count); - player.getWorld().addEntity(entity); - } - } - } - - @Command(helpText = "/spawn [monster id] [count] - Creates {count} amount of {item id}") - public static class Spawn extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int monsterId = 0, count = 1, level = 1; - - try { - monsterId = Integer.parseInt(split[0]); - } catch (Exception e) { - monsterId = 0; - } - - try { - level = Math.max(Math.min(Integer.parseInt(split[1]), 200), 1); - } catch (Exception e) { - level = 1; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[2]), 1000), 1); - } catch (Exception e) { - count = 1; - } - - // Give - MonsterData monsterData = GenshinData.getMonsterDataMap().get(monsterId); - - if (monsterData == null) { - player.dropMessage("Error: Monster data not found"); - return; - } - - float range = (5f + (.1f * count)); - for (int i = 0; i < count; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityMonster entity = new EntityMonster(player.getWorld(), monsterData, pos, level); - player.getWorld().addEntity(entity); - } - } - } - - @Command(helpText = "/killall") - public static class KillAll extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - List toRemove = new LinkedList<>(); - for (GenshinEntity entity : player.getWorld().getEntities().values()) { - if (entity instanceof EntityMonster) { - toRemove.add(entity); - } - } - toRemove.forEach(e -> player.getWorld().killEntity(e, 0)); - } - } - - @Command(helpText = "/resetconst - Resets all constellations for the currently active character") - public static class ResetConst extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - - if (entity == null) { - return; - } - - GenshinAvatar avatar = entity.getAvatar(); - - avatar.getTalentIdList().clear(); - avatar.setCoreProudSkillLevel(0); - avatar.recalcStats(); - avatar.save(); - - player.dropMessage("Constellations for " + entity.getAvatar().getAvatarData().getName() + " have been reset. Please relogin to see changes."); - } - } - - @Command(helpText = "/godmode - Prevents you from taking damage") - public static class Godmode extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - player.setGodmode(!player.hasGodmode()); - player.dropMessage("Godmode is now " + (player.hasGodmode() ? "ON" : "OFF")); - } - } - - @Command(helpText = "/sethp [hp]") - public static class Sethp extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int hp = 0; - - try { - hp = Math.max(Integer.parseInt(split[0]), 1); - } catch (Exception e) { - hp = 1; - } - - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - - if (entity == null) { - return; - } - - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, hp); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - } - } - - @Command(aliases = {"clearart"}, helpText = "/clearartifacts") - public static class ClearArtifacts extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - List toRemove = new LinkedList<>(); - for (GenshinItem item : player.getInventory().getItems().values()) { - if (item.getItemType() == ItemType.ITEM_RELIQUARY && item.getLevel() == 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) { - toRemove.add(item); - } - } - - player.getInventory().removeItems(toRemove); - } - } -} diff --git a/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java b/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java deleted file mode 100644 index 2039d5227..000000000 --- a/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java +++ /dev/null @@ -1,171 +0,0 @@ -package emu.grasscutter.commands; - -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.inventory.GenshinItem; -import emu.grasscutter.utils.Crypto; -import emu.grasscutter.utils.Utils; - -public class ServerCommands { - private static HashMap list = new HashMap<>(); - - static { - try { - // Look for classes - for (Class cls : ServerCommands.class.getDeclaredClasses()) { - // Get non abstract classes - if (!Modifier.isAbstract(cls.getModifiers())) { - String commandName = cls.getSimpleName().toLowerCase(); - list.put(commandName, (ServerCommand) cls.newInstance()); - } - - } - } catch (Exception e) { - - } - } - - public static void handle(String msg) { - String[] split = msg.split(" "); - - // End if invalid - if (split.length == 0) { - return; - } - - // - String first = split[0].toLowerCase(); - ServerCommand c = ServerCommands.list.get(first); - - if (c != null) { - // Execute - int len = Math.min(first.length() + 1, msg.length()); - c.execute(msg.substring(len)); - } - } - - public static abstract class ServerCommand { - public abstract void execute(String raw); - } - - // ================ Commands ================ - - public static class Reload extends ServerCommand { - @Override - public void execute(String raw) { - Grasscutter.getLogger().info("Reloading config."); - Grasscutter.loadConfig(); - Grasscutter.getDispatchServer().loadQueries(); - Grasscutter.getLogger().info("Reload complete."); - } - } - - public static class sendMsg extends ServerCommand { - @Override - public void execute(String raw) { - List split = Arrays.asList(raw.split(" ")); - - if (split.size() < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - String playerID = split.get(0); - String message = split.stream().skip(1).collect(Collectors.joining(" ")); - - - emu.grasscutter.game.Account account = DatabaseHelper.getAccountByPlayerId(Integer.parseInt(playerID)); - if (account != null) { - GenshinPlayer player = Grasscutter.getGameServer().getPlayerById(Integer.parseInt(playerID)); - if(player != null) { - player.dropMessage(message); - Grasscutter.getLogger().info(String.format("Successfully sent message to %s: %s", playerID, message)); - } else { - Grasscutter.getLogger().error("Player not online"); - } - } else { - Grasscutter.getLogger().error(String.format("Player %s does not exist", playerID)); - } - } - } - - public static class Account extends ServerCommand { - @Override - public void execute(String raw) { - String[] split = raw.split(" "); - - if (split.length < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - String command = split[0].toLowerCase(); - String username = split[1]; - - switch (command) { - case "create": - if (split.length < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - int reservedId = 0; - try { - reservedId = Integer.parseInt(split[2]); - } catch (Exception e) { - reservedId = 0; - } - - emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, reservedId); - if (account != null) { - Grasscutter.getLogger().info("Account created" + (reservedId > 0 ? " with an id of " + reservedId : "")); - } else { - Grasscutter.getLogger().error("Account already exists"); - } - break; - case "delete": - boolean success = DatabaseHelper.deleteAccount(username); - - if (success) { - Grasscutter.getLogger().info("Account deleted"); - } - break; - /* - case "setpw": - case "setpass": - case "setpassword": - if (split.length < 3) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - account = DatabaseHelper.getAccountByName(username); - - if (account == null) { - Grasscutter.getLogger().error("No account found!"); - return; - } - - token = split[2]; - token = PasswordHelper.hashPassword(token); - - account.setPassword(token); - DatabaseHelper.saveAccount(account); - - Grasscutter.getLogger().info("Password set"); - break; - */ - } - } - } -} diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index c9ccb551c..0e34c120d 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -7,7 +7,7 @@ import java.io.FileWriter; import java.io.InputStreamReader; import java.net.InetSocketAddress; -import emu.grasscutter.commands.CommandMap; +import emu.grasscutter.command.CommandMap; import emu.grasscutter.utils.Utils; import org.reflections.Reflections; import org.slf4j.LoggerFactory; diff --git a/src/main/java/emu/grasscutter/command/Command.java b/src/main/java/emu/grasscutter/command/Command.java new file mode 100644 index 000000000..d8a57e1a8 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/Command.java @@ -0,0 +1,17 @@ +package emu.grasscutter.command; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Command { + String label() default ""; + + String usage() default "No usage specified"; + + String description() default "No description specified"; + + String[] aliases() default {}; + + String permission() default ""; +} diff --git a/src/main/java/emu/grasscutter/command/CommandHandler.java b/src/main/java/emu/grasscutter/command/CommandHandler.java new file mode 100644 index 000000000..187dc0b6a --- /dev/null +++ b/src/main/java/emu/grasscutter/command/CommandHandler.java @@ -0,0 +1,25 @@ +package emu.grasscutter.command; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +public interface CommandHandler { + /** + * Send a message to the target. + * + * @param player The player to send the message to, or null for the server console. + * @param message The message to send. + */ + static void sendMessage(GenshinPlayer player, String message) { + if (player == null) { + Grasscutter.getLogger().info(message); + } else { + player.dropMessage(message); + } + } + + default void onCommand(GenshinPlayer sender, List args) { + } +} diff --git a/src/main/java/emu/grasscutter/commands/CommandMap.java b/src/main/java/emu/grasscutter/command/CommandMap.java similarity index 75% rename from src/main/java/emu/grasscutter/commands/CommandMap.java rename to src/main/java/emu/grasscutter/command/CommandMap.java index a802551ce..d61e46833 100644 --- a/src/main/java/emu/grasscutter/commands/CommandMap.java +++ b/src/main/java/emu/grasscutter/command/CommandMap.java @@ -1,4 +1,4 @@ -package emu.grasscutter.commands; +package emu.grasscutter.command; import emu.grasscutter.Grasscutter; import emu.grasscutter.game.Account; @@ -7,75 +7,89 @@ import org.reflections.Reflections; import java.util.*; -@SuppressWarnings("UnusedReturnValue") +@SuppressWarnings({"UnusedReturnValue", "unused"}) public final class CommandMap { + private final Map commands = new HashMap<>(); + private final Map annotations = new HashMap<>(); + public CommandMap() { + this(false); + } + + public CommandMap(boolean scan) { + if (scan) this.scan(); + } + public static CommandMap getInstance() { return Grasscutter.getGameServer().getCommandMap(); } - - private final Map commands = new HashMap<>(); - private final Map annotations = new HashMap<>(); /** * Register a command handler. - * @param label The command label. + * + * @param label The command label. * @param command The command handler. * @return Instance chaining. */ public CommandMap registerCommand(String label, CommandHandler command) { Grasscutter.getLogger().debug("Registered command: " + label); - + // Get command data. Command annotation = command.getClass().getAnnotation(Command.class); this.annotations.put(label, annotation); this.commands.put(label, command); - + // Register aliases. - if(annotation.aliases().length > 0) { + if (annotation.aliases().length > 0) { for (String alias : annotation.aliases()) { this.commands.put(alias, command); this.annotations.put(alias, annotation); } - } return this; + } + return this; } /** * Removes a registered command handler. + * * @param label The command label. * @return Instance chaining. */ public CommandMap unregisterCommand(String label) { Grasscutter.getLogger().debug("Unregistered command: " + label); CommandHandler handler = this.commands.get(label); - if(handler == null) return this; - + if (handler == null) return this; + Command annotation = handler.getClass().getAnnotation(Command.class); this.annotations.remove(label); this.commands.remove(label); - + // Unregister aliases. - if(annotation.aliases().length > 0) { + if (annotation.aliases().length > 0) { for (String alias : annotation.aliases()) { this.commands.remove(alias); this.annotations.remove(alias); } } - + return this; } /** * Returns a list of all registered commands. + * * @return All command handlers as a list. */ public List getHandlersAsList() { return new LinkedList<>(this.commands.values()); } - public HashMap getHandlers() { return new LinkedHashMap<>(this.commands); } + public HashMap getHandlers() { + return new LinkedHashMap<>(this.commands); + } /** * Returns a handler by label/alias. + * * @param label The command label. * @return The command handler. */ @@ -85,58 +99,44 @@ public final class CommandMap { /** * Invoke a command handler with the given arguments. - * @param player The player invoking the command or null for the server console. + * + * @param player The player invoking the command or null for the server console. * @param rawMessage The messaged used to invoke the command. */ public void invoke(GenshinPlayer player, String rawMessage) { rawMessage = rawMessage.trim(); - if(rawMessage.length() == 0) { + if (rawMessage.length() == 0) { CommandHandler.sendMessage(player, "No command specified."); } - + // Remove prefix if present. - if(!Character.isLetter(rawMessage.charAt(0))) + if (!Character.isLetter(rawMessage.charAt(0))) rawMessage = rawMessage.substring(1); - + // Parse message. String[] split = rawMessage.split(" "); List args = new LinkedList<>(Arrays.asList(split)); String label = args.remove(0); - + // Get command handler. CommandHandler handler = this.commands.get(label); - if(handler == null) { - CommandHandler.sendMessage(player, "Unknown command: " + label); return; + if (handler == null) { + CommandHandler.sendMessage(player, "Unknown command: " + label); + return; } - + // Check for permission. - if(player != null) { + if (player != null) { String permissionNode = this.annotations.get(label).permission(); Account account = player.getAccount(); - if(permissionNode != "" && !account.hasPermission(permissionNode)) { - CommandHandler.sendMessage(player, "You do not have permission to run this command."); return; + if (!Objects.equals(permissionNode, "") && !account.hasPermission(permissionNode)) { + CommandHandler.sendMessage(player, "You do not have permission to run this command."); + return; } } - - // Execution power check. - Command.Execution executionPower = this.annotations.get(label).execution(); - if(player == null && executionPower == Command.Execution.PLAYER) { - CommandHandler.sendMessage(null, "Run this command in-game."); return; - } else if (player != null && executionPower == Command.Execution.CONSOLE) { - CommandHandler.sendMessage(player, "This command can only be run from the console."); return; - } - + // Invoke execute method for handler. - if(player == null) handler.execute(args); - else handler.execute(player, args); - } - - public CommandMap() { - this(false); - } - - public CommandMap(boolean scan) { - if(scan) this.scan(); + handler.onCommand(player, args); } /** diff --git a/src/main/java/emu/grasscutter/command/commands/Account.java b/src/main/java/emu/grasscutter/command/commands/Account.java new file mode 100644 index 000000000..4d4a80db4 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Account.java @@ -0,0 +1,61 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "account", usage = "account [uid]", + description = "Modify user accounts") +public class Account implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender != null) { + CommandHandler.sendMessage(sender, "This command can only be run from the console."); + return; + } + + if (args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: account [uid]"); + return; + } + + String action = args.get(0); + String username = args.get(1); + + switch (action) { + default: + CommandHandler.sendMessage(null, "Usage: account [uid]"); + return; + case "create": + int uid = 0; + if (args.size() > 2) { + try { + uid = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid UID."); + return; + } + } + + emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, uid); + if (account == null) { + CommandHandler.sendMessage(null, "Account already exists."); + return; + } else { + CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); + account.addPermission("*"); // Grant the player superuser permissions. + } + return; + case "delete": + if (DatabaseHelper.deleteAccount(username)) { + CommandHandler.sendMessage(null, "Account deleted."); + } else { + CommandHandler.sendMessage(null, "Account not found."); + } + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Broadcast.java b/src/main/java/emu/grasscutter/command/commands/Broadcast.java new file mode 100644 index 000000000..f8d08eb9c --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Broadcast.java @@ -0,0 +1,29 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "broadcast", usage = "broadcast ", + description = "Sends a message to all the players", aliases = {"b"}, permission = "server.broadcast") +public class Broadcast implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: broadcast "); + return; + } + + String message = String.join(" ", args.subList(0, args.size())); + + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + CommandHandler.sendMessage(p, message); + } + + CommandHandler.sendMessage(sender, "Message sent."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ChangeScene.java b/src/main/java/emu/grasscutter/command/commands/ChangeScene.java new file mode 100644 index 000000000..fe1521fba --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/ChangeScene.java @@ -0,0 +1,36 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "changescene", usage = "changescene ", + description = "Changes your scene", aliases = {"scene"}, permission = "player.changescene") +public class ChangeScene implements CommandHandler { + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: changescene "); + return; + } + + try { + int sceneId = Integer.parseInt(args.get(0)); + boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, sender.getPos()); + + CommandHandler.sendMessage(sender, "Changed to scene " + sceneId); + if (!result) { + CommandHandler.sendMessage(sender, "Scene does not exist or you are already in it"); + } + } catch (Exception e) { + CommandHandler.sendMessage(sender, "Usage: changescene "); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java b/src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java new file mode 100644 index 000000000..2069b0466 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java @@ -0,0 +1,30 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.inventory.Inventory; +import emu.grasscutter.game.inventory.ItemType; + +import java.util.List; + +@Command(label = "clearartifacts", usage = "clearartifacts", + description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory", + aliases = {"clearart"}, permission = "player.clearartifacts") +public class ClearArtifacts implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: clear player's artifacts from console or other players + } + + Inventory playerInventory = sender.getInventory(); + playerInventory.getItems().values().stream() + .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) + .filter(item -> item.getLevel() == 1 && item.getExp() == 0) + .filter(item -> !item.isLocked() && !item.isEquipped()) + .forEach(item -> playerInventory.removeItem(item, item.getCount())); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Drop.java b/src/main/java/emu/grasscutter/command/commands/Drop.java new file mode 100644 index 000000000..3ef3bc840 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Drop.java @@ -0,0 +1,56 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.utils.Position; + +import java.util.List; + +@Command(label = "drop", usage = "drop [amount]", + description = "Drops an item near you", aliases = {"d", "dropitem"}, permission = "server.drop") +public class Drop implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: drop [amount]"); + return; + } + + try { + int item = Integer.parseInt(args.get(0)); + int amount = 1; + if (args.size() > 1) amount = Integer.parseInt(args.get(1)); + + ItemData itemData = GenshinData.getItemDataMap().get(item); + if (itemData == null) { + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + + if (itemData.isEquip()) { + float range = (5f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, pos, 1); + sender.getScene().addEntity(entity); + } + } else { + EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, sender.getPos().clone().addY(3f), amount); + sender.getScene().addEntity(entity); + } + CommandHandler.sendMessage(sender, String.format("Dropped %s of %s.", amount, item)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + } + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/Give.java b/src/main/java/emu/grasscutter/command/commands/Give.java new file mode 100644 index 000000000..43f1961cc --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Give.java @@ -0,0 +1,112 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; + +import java.util.LinkedList; +import java.util.List; + +@Command(label = "give", usage = "give [player] [amount]", + description = "Gives an item to you or the specified player", aliases = {"g", "item", "giveitem"}, permission = "player.give") +public class Give implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + int target, item, amount = 1; + + if (sender == null && args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: give [amount]"); + return; + } + + switch (args.size()) { + default: // *No args* + CommandHandler.sendMessage(sender, "Usage: give [player] [amount]"); + return; + case 1: // + try { + item = Integer.parseInt(args.get(0)); + target = sender.getUid(); + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + break; + case 2: // [amount] | [player] + try { + target = Integer.parseInt(args.get(0)); + + if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { + target = sender.getUid(); + item = Integer.parseInt(args.get(0)); + amount = Integer.parseInt(args.get(1)); + } else { + item = Integer.parseInt(args.get(1)); + } + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + return; + } + break; + case 3: // [player] [amount] + try { + target = Integer.parseInt(args.get(0)); + + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + return; + } + + item = Integer.parseInt(args.get(1)); + amount = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + return; + } + break; + } + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + ItemData itemData = GenshinData.getItemDataMap().get(item); + if (itemData == null) { + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + + this.item(targetPlayer, itemData, amount); + + CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target)); + } + + private void item(GenshinPlayer player, ItemData itemData, int amount) { + GenshinItem genshinItem = new GenshinItem(itemData); + if (itemData.isEquip()) { + List items = new LinkedList<>(); + for (int i = 0; i < amount; i++) { + items.add(genshinItem); + } + player.getInventory().addItems(items); + player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); + } else { + genshinItem.setCount(amount); + player.getInventory().addItem(genshinItem); + player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop)); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/GiveChar.java b/src/main/java/emu/grasscutter/command/commands/GiveChar.java new file mode 100644 index 000000000..7eaa56a47 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/GiveChar.java @@ -0,0 +1,93 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.AvatarData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.avatar.GenshinAvatar; + +import java.util.List; + +@Command(label = "givechar", usage = "givechar [level]", + description = "Gives the player a specified character", aliases = {"givec"}, permission = "player.givechar") +public class GiveChar implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + int target, avatarId, level = 1, ascension; + + if (sender == null && args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: givechar [amount]"); + return; + } + + switch (args.size()) { + default: + CommandHandler.sendMessage(sender, "Usage: givechar [level]"); + return; + case 2: + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { + target = sender.getUid(); + level = Integer.parseInt(args.get(1)); + avatarId = Integer.parseInt(args.get(0)); + } else { + avatarId = Integer.parseInt(args.get(1)); + } + } catch (NumberFormatException ignored) { + // TODO: Parse from avatar name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid avatar or player ID."); + return; + } + break; + case 3: + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + return; + } + + avatarId = Integer.parseInt(args.get(1)); + level = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + // TODO: Parse from avatar name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid avatar or player ID."); + return; + } + break; + } + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId); + if (avatarData == null) { + CommandHandler.sendMessage(sender, "Invalid avatar id."); + return; + } + + // Calculate ascension level. + if (level <= 40) { + ascension = (int) Math.ceil(level / 20f); + } else { + ascension = (int) Math.ceil(level / 10f) - 3; + } + + GenshinAvatar avatar = new GenshinAvatar(avatarId); + avatar.setLevel(level); + avatar.setPromoteLevel(ascension); + + // This will handle stats and talents + avatar.recalcStats(); + + targetPlayer.addAvatar(avatar); + CommandHandler.sendMessage(sender, String.format("Given %s to %s.", avatarId, target)); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/GodMode.java b/src/main/java/emu/grasscutter/command/commands/GodMode.java new file mode 100644 index 000000000..491bf0ba8 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/GodMode.java @@ -0,0 +1,22 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "godmode", usage = "godmode [playerId]", + description = "Prevents you from taking damage", permission = "player.godmode") +public class GodMode implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: toggle player's godmode statue from console or other players + } + sender.setGodmode(!sender.inGodmode()); + sender.dropMessage("Godmode is now " + (sender.inGodmode() ? "enabled" : "disabled") + "."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Help.java b/src/main/java/emu/grasscutter/command/commands/Help.java new file mode 100644 index 000000000..3350160a7 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Help.java @@ -0,0 +1,91 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.command.CommandMap; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.*; + +@Command(label = "help", usage = "help [command]", + description = "Sends the help message or shows information about a specified command") +public class Help implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer player, List args) { + if (args.size() < 1) { + HashMap handlers = CommandMap.getInstance().getHandlers(); + List annotations = new ArrayList<>(); + for (String key : handlers.keySet()) { + Command annotation = handlers.get(key).getClass().getAnnotation(Command.class); + + if (!Arrays.asList(annotation.aliases()).contains(key)) { + if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) + continue; + annotations.add(annotation); + } + } + + SendAllHelpMessage(player, annotations); + } else { + String command = args.get(0); + CommandHandler handler = CommandMap.getInstance().getHandler(command); + StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n"); + if (handler == null) { + builder.append("No command found."); + } else { + Command annotation = handler.getClass().getAnnotation(Command.class); + + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) { + builder.append("\n Warning: You do not have permission to run this command."); + } + } + + CommandHandler.sendMessage(player, builder.toString()); + } + } + + void SendAllHelpMessage(GenshinPlayer player, List annotations) { + if (player == null) { + StringBuilder builder = new StringBuilder("\nAvailable commands:\n"); + annotations.forEach(annotation -> { + builder.append(annotation.label()).append("\n"); + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + + builder.append("\n"); + }); + + CommandHandler.sendMessage(null, builder.toString()); + } else { + CommandHandler.sendMessage(player, "Available commands:"); + annotations.forEach(annotation -> { + StringBuilder builder = new StringBuilder(annotation.label()).append("\n"); + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + + CommandHandler.sendMessage(player, builder.toString()); + }); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Kick.java b/src/main/java/emu/grasscutter/command/commands/Kick.java new file mode 100644 index 000000000..d1ef5077e --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Kick.java @@ -0,0 +1,31 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "kick", usage = "kick ", + description = "Kicks the specified player from the server (WIP)", permission = "server.kick") +public class Kick implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + int target = Integer.parseInt(args.get(0)); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + if (sender != null) { + CommandHandler.sendMessage(sender, String.format("Player [%s:%s] has kicked player [%s:%s]", sender.getAccount().getPlayerId(), sender.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); + } + CommandHandler.sendMessage(sender, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername())); + + targetPlayer.getSession().close(); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/KillAll.java b/src/main/java/emu/grasscutter/command/commands/KillAll.java new file mode 100644 index 000000000..540c01ce3 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/KillAll.java @@ -0,0 +1,64 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.GenshinScene; +import emu.grasscutter.game.entity.EntityMonster; + +import java.util.List; + +@Command(label = "killall", usage = "killall [playerUid] [sceneId]", + description = "Kill all entities", permission = "server.killall") +public class KillAll implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + GenshinScene scene; + GenshinPlayer genshinPlayer; + + try { + switch (args.size()) { + case 0: // *No args* + if (sender == null) { + CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); + return; + } + scene = sender.getScene(); + break; + case 1: // [playerUid] + genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0))); + if (genshinPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found or offline."); + return; + } + scene = genshinPlayer.getScene(); + break; + case 2: // [playerUid] [sceneId] + genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0))); + if (genshinPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found or offline."); + return; + } + GenshinScene genshinScene = sender.getWorld().getSceneById(Integer.parseInt(args.get(1))); + if (genshinScene == null) { + CommandHandler.sendMessage(sender, "Scene not found in player world"); + return; + } + scene = genshinScene; + break; + default: + CommandHandler.sendMessage(sender, "Usage: killall [playerUid] [sceneId]"); + return; + } + + scene.getEntities().values().stream() + .filter(entity -> entity instanceof EntityMonster) + .forEach(entity -> scene.killEntity(entity, 0)); + CommandHandler.sendMessage(sender, "Killing all monsters in scene " + scene.getId()); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid arguments."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Permission.java b/src/main/java/emu/grasscutter/command/commands/Permission.java new file mode 100644 index 000000000..02c047f48 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Permission.java @@ -0,0 +1,50 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "permission", usage = "permission ", + description = "Grants or removes a permission for a user", permission = "*") +public class Permission implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (args.size() < 3) { + CommandHandler.sendMessage(sender, "Usage: permission "); + return; + } + + String action = args.get(0); + String username = args.get(1); + String permission = args.get(2); + + Account account = Grasscutter.getGameServer().getAccountByName(username); + if (account == null) { + CommandHandler.sendMessage(sender, "Account not found."); + return; + } + + switch (action) { + default: + CommandHandler.sendMessage(sender, "Usage: permission "); + break; + case "add": + if (account.addPermission(permission)) { + CommandHandler.sendMessage(sender, "Permission added."); + } else CommandHandler.sendMessage(sender, "They already have this permission!"); + break; + case "remove": + if (account.removePermission(permission)) { + CommandHandler.sendMessage(sender, "Permission removed."); + } else CommandHandler.sendMessage(sender, "They don't have this permission!"); + break; + } + + account.save(); + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/Reload.java b/src/main/java/emu/grasscutter/command/commands/Reload.java new file mode 100644 index 000000000..587ffcd9c --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Reload.java @@ -0,0 +1,21 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "reload", usage = "reload", + description = "Reload server config", permission = "server.reload") +public class Reload implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + CommandHandler.sendMessage(sender, "Reloading config."); + Grasscutter.loadConfig(); + Grasscutter.getDispatchServer().loadQueries(); + CommandHandler.sendMessage(sender, "Reload complete."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ResetConst.java b/src/main/java/emu/grasscutter/command/commands/ResetConst.java new file mode 100644 index 000000000..cabc00b64 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/ResetConst.java @@ -0,0 +1,45 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.avatar.GenshinAvatar; +import emu.grasscutter.game.entity.EntityAvatar; + +import java.util.List; + +@Command(label = "resetconst", usage = "resetconst [all]", + description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.", + aliases = {"resetconstellation"}, permission = "player.resetconstellation") +public class ResetConst implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { + sender.getAvatars().forEach(this::resetConstellation); + sender.dropMessage("Reset all avatars' constellations."); + } else { + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + if (entity == null) { + return; + } + + GenshinAvatar avatar = entity.getAvatar(); + this.resetConstellation(avatar); + + sender.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes."); + } + } + + private void resetConstellation(GenshinAvatar avatar) { + avatar.getTalentIdList().clear(); + avatar.setCoreProudSkillLevel(0); + avatar.recalcStats(); + avatar.save(); + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/SendMessage.java b/src/main/java/emu/grasscutter/command/commands/SendMessage.java new file mode 100644 index 000000000..3d2fd7b70 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/SendMessage.java @@ -0,0 +1,37 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "say", usage = "say ", description = "Sends a message to a player as the server", + aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage") +public class SendMessage implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: sendmessage "); + return; + } + + try { + int target = Integer.parseInt(args.get(0)); + String message = String.join(" ", args.subList(1, args.size())); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + CommandHandler.sendMessage(targetPlayer, message); + CommandHandler.sendMessage(sender, "Message sent."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + } + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/SetHealth.java b/src/main/java/emu/grasscutter/command/commands/SetHealth.java new file mode 100644 index 000000000..2daf792ef --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/SetHealth.java @@ -0,0 +1,42 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; + +import java.util.List; + +@Command(label = "sethealth", usage = "sethealth ", + description = "Sets your health to the specified value", aliases = {"sethp"}, permission = "player.sethealth") +public class SetHealth implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: set player's health from console or other players + } + + if (args.size() < 1) { + CommandHandler.sendMessage(null, "Usage: sethealth "); + return; + } + + try { + int health = Integer.parseInt(args.get(0)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + if (entity == null) { + return; + } + + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + sender.dropMessage("Health set to " + health + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid health value."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java b/src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java new file mode 100644 index 000000000..69eeae1d9 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java @@ -0,0 +1,39 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.props.PlayerProperty; + +import java.util.List; + +@Command(label = "setworldlevel", usage = "setworldlevel ", + description = "Sets your world level (Relog to see proper effects)", + aliases = {"setworldlvl"}, permission = "player.setworldlevel") +public class SetWorldLevel implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: set player's world level from console or other players + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: setworldlevel "); + return; + } + + try { + int level = Integer.parseInt(args.get(0)); + + // Set in both world and player props + sender.getWorld().setWorldLevel(level); + sender.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level); + + sender.dropMessage("World level set to " + level + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid world level."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Spawn.java b/src/main/java/emu/grasscutter/command/commands/Spawn.java new file mode 100644 index 000000000..def06c362 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Spawn.java @@ -0,0 +1,51 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.utils.Position; + +import java.util.List; + +@Command(label = "spawn", usage = "spawn [level] [amount]", + description = "Spawns an entity near you", permission = "server.spawn") +public class Spawn implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: spawn [amount]"); + return; + } + + try { + int entity = Integer.parseInt(args.get(0)); + int level = args.size() > 1 ? Integer.parseInt(args.get(1)) : 1; + int amount = args.size() > 2 ? Integer.parseInt(args.get(2)) : 1; + + MonsterData entityData = GenshinData.getMonsterDataMap().get(entity); + if (entityData == null) { + CommandHandler.sendMessage(sender, "Invalid entity id."); + return; + } + + float range = (5f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityMonster monster = new EntityMonster(sender.getScene(), entityData, pos, level); + sender.getScene().addEntity(monster); + } + CommandHandler.sendMessage(sender, String.format("Spawned %s of %s.", amount, entity)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Stop.java b/src/main/java/emu/grasscutter/command/commands/Stop.java new file mode 100644 index 000000000..21676cb7d --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Stop.java @@ -0,0 +1,23 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "stop", usage = "stop", + description = "Stops the server", permission = "server.stop") +public class Stop implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + CommandHandler.sendMessage(null, "Server shutting down..."); + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + CommandHandler.sendMessage(p, "Server shutting down..."); + } + + System.exit(1); + } +} diff --git a/src/main/java/emu/grasscutter/commands/Command.java b/src/main/java/emu/grasscutter/commands/Command.java deleted file mode 100644 index d26d7fe29..000000000 --- a/src/main/java/emu/grasscutter/commands/Command.java +++ /dev/null @@ -1,25 +0,0 @@ -package emu.grasscutter.commands; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -public @interface Command { - String label() default ""; - - String usage() default "No usage specified"; - - String description() default "No description specified"; - - String[] aliases() default {}; - - Execution execution() default Execution.ALL; - - String permission() default ""; - - enum Execution { - ALL, - CONSOLE, - PLAYER - } -} diff --git a/src/main/java/emu/grasscutter/commands/CommandHandler.java b/src/main/java/emu/grasscutter/commands/CommandHandler.java deleted file mode 100644 index 97bd8c81f..000000000 --- a/src/main/java/emu/grasscutter/commands/CommandHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -package emu.grasscutter.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -public interface CommandHandler { - /* Invoked on player execution. */ - default void execute(GenshinPlayer player, List args) { } - /* Invoked on server execution. */ - default void execute(List args) { } - - /* - * Utilities. - */ - - /** - * Send a message to the target. - * @param player The player to send the message to, or null for the server console. - * @param message The message to send. - */ - static void sendMessage(GenshinPlayer player, String message) { - if(player == null) { - Grasscutter.getLogger().info(message); - } else player.dropMessage(message); - } -} diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java deleted file mode 100644 index 779e91ef9..000000000 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ /dev/null @@ -1,539 +0,0 @@ -package emu.grasscutter.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.data.def.AvatarSkillDepotData; -import emu.grasscutter.data.def.MonsterData; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.GenshinScene; -import emu.grasscutter.game.World; -import emu.grasscutter.game.avatar.GenshinAvatar; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.EntityItem; -import emu.grasscutter.game.entity.EntityMonster; -import emu.grasscutter.game.inventory.GenshinItem; -import emu.grasscutter.game.inventory.Inventory; -import emu.grasscutter.game.inventory.ItemType; -import emu.grasscutter.game.props.ActionReason; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.game.props.PlayerProperty; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; -import emu.grasscutter.utils.Position; - -import java.util.LinkedList; -import java.util.List; - -/** - * A container for player-related commands. - */ -public final class PlayerCommands { - @Command(label = "give", aliases = {"g", "item", "giveitem"}, - usage = "give [player] [amount]", description = "Gives an item to you or the specified player", permission = "player.give") - public static class GiveCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - int target, item, amount = 1; - - switch(args.size()) { - default: - CommandHandler.sendMessage(player, "Usage: give [amount]"); - return; - case 1: - try { - item = Integer.parseInt(args.get(0)); - target = player.getAccount().getPlayerId(); - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid item id."); - return; - } - break; - case 2: - try { - target = Integer.parseInt(args.get(0)); - - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - target = player.getUid(); amount = Integer.parseInt(args.get(1)); - item = Integer.parseInt(args.get(0)); - } else { - item = Integer.parseInt(args.get(1)); - } - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid item or player ID."); - return; - } - break; - case 3: - try { - target = Integer.parseInt(args.get(0)); - - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - CommandHandler.sendMessage(player, "Invalid player ID."); return; - } - - item = Integer.parseInt(args.get(1)); - amount = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid item or player ID."); - return; - } - break; - } - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); return; - } - - ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(player, "Invalid item id."); return; - } - - this.item(targetPlayer, itemData, amount); - } - - /** - * give [player] [itemId|itemName] [amount] - */ - @Override public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: give [amount]"); - return; - } - - try { - int target = Integer.parseInt(args.get(0)); - int item = Integer.parseInt(args.get(1)); - int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; - } - - ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(null, "Invalid item id."); return; - } - - this.item(targetPlayer, itemData, amount); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid item or player ID."); - } - } - - private void item(GenshinPlayer player, ItemData itemData, int amount) { - GenshinItem genshinItem = new GenshinItem(itemData); - if(itemData.isEquip()) { - List items = new LinkedList<>(); - for(int i = 0; i < amount; i++) { - items.add(genshinItem); - } player.getInventory().addItems(items); - player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); - } else { - genshinItem.setCount(amount); - player.getInventory().addItem(genshinItem); - player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop)); - } - } - } - - @Command(label = "drop", aliases = {"d", "dropitem"}, - usage = "drop [amount]", - execution = Command.Execution.PLAYER, description = "Drops an item near you", permission = "server.drop") - public static class DropCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: drop [amount]"); - return; - } - - try { - int item = Integer.parseInt(args.get(0)); - int amount = 1; if(args.size() > 1) amount = Integer.parseInt(args.get(1)); - - ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(player, "Invalid item id."); return; - } - - if (itemData.isEquip()) { - float range = (5f + (.1f * amount)); - for (int i = 0; i < amount; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityItem entity = new EntityItem(player.getScene(), player, itemData, pos, 1); - player.getScene().addEntity(entity); - } - } else { - EntityItem entity = new EntityItem(player.getScene(), player, itemData, player.getPos().clone().addY(3f), amount); - player.getScene().addEntity(entity); - } - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(player, "Invalid item or player ID."); - } - } - } - - @Command(label = "givechar", aliases = { "givec" }, usage = "givechar [level]", - description = "Gives the player a specified character", permission = "player.givechar") - public static class GiveCharCommand implements CommandHandler { - @Override public void execute(GenshinPlayer player, List args) { - int target, avatarId, level = 1, ascension = 1; - - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: givechar [level]"); - return; - } - - switch(args.size()) { - default: - CommandHandler.sendMessage(player, "Usage: givechar [level]"); - return; - case 2: - try { - target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - target = player.getUid(); - level = Integer.parseInt(args.get(1)); - avatarId = Integer.parseInt(args.get(0)); - } else { - avatarId = Integer.parseInt(args.get(1)); - } - } catch (NumberFormatException ignored) { - // TODO: Parse from avatar name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid avatar or player ID."); - return; - } - break; - case 3: - try { - target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - CommandHandler.sendMessage(player, "Invalid player ID."); return; - } - - avatarId = Integer.parseInt(args.get(1)); - level = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - // TODO: Parse from avatar name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid avatar or player ID."); - return; - } - break; - } - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); return; - } - - AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId); - if(avatarData == null) { - CommandHandler.sendMessage(player, "Invalid avatar id."); return; - } - - // Calculate ascension level. - if (level <= 40) { - ascension = (int) Math.ceil(level / 20f); - } else { - ascension = (int) Math.ceil(level / 10f) - 3; - } - - GenshinAvatar avatar = new GenshinAvatar(avatarId); - avatar.setLevel(level); - avatar.setPromoteLevel(ascension); - - // This will handle stats and talents - avatar.recalcStats(); - - targetPlayer.addAvatar(avatar); - } - - @Override - public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: givechar [amount]"); - return; - } - - try { - int target = Integer.parseInt(args.get(0)); - int avatarID = Integer.parseInt(args.get(1)); - int level = 1; if(args.size() > 2) level = Integer.parseInt(args.get(2)); - int ascension; - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; - } - - AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarID); - if(avatarData == null) { - CommandHandler.sendMessage(null, "Invalid avatar id."); return; - } - - // Calculate ascension level. - if (level <= 40) { - ascension = (int) Math.ceil(level / 20f); - } else { - ascension = (int) Math.ceil(level / 10f) - 3; - } - - GenshinAvatar avatar = new GenshinAvatar(avatarID); - avatar.setLevel(level); - avatar.setPromoteLevel(ascension); - - // This will handle stats and talents - avatar.recalcStats(); - - targetPlayer.addAvatar(avatar); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid item or player ID."); - } - } - } - - @Command(label = "spawn", execution = Command.Execution.PLAYER, - usage = "spawn [level] [amount]", description = "Spawns an entity near you", permission = "server.spawn") - public static class SpawnCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: spawn [amount]"); - return; - } - - try { - int entity = Integer.parseInt(args.get(0)); - int level = 1; if(args.size() > 1) level = Integer.parseInt(args.get(1)); - int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); - - MonsterData entityData = GenshinData.getMonsterDataMap().get(entity); - if(entityData == null) { - CommandHandler.sendMessage(null, "Invalid entity id."); return; - } - - float range = (5f + (.1f * amount)); - for (int i = 0; i < amount; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityMonster monster = new EntityMonster(player.getScene(), entityData, pos, level); - player.getScene().addEntity(monster); - } - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid item or player ID."); - } - } - } - - @Command(label = "killall", - usage = "killall [playerUid] [sceneId]", description = "Kill all entities", permission = "server.killall") - public static class KillAllCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - GenshinScene scene = player.getScene(); - scene.getEntities().values().stream() - .filter(entity -> entity instanceof EntityMonster) - .forEach(entity -> scene.killEntity(entity, 0)); - CommandHandler.sendMessage(null, "Killing all monsters in scene " + scene.getId()); - } - - @Override - public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); return; - } - - try { - int playerUid = Integer.parseInt(args.get(0)); - int sceneId = Integer.parseInt(args.get(1)); - - GenshinPlayer player = Grasscutter.getGameServer().getPlayerByUid(playerUid); - if (player == null) { - CommandHandler.sendMessage(null, "Player not found or offline."); - return; - } - - GenshinScene scene = player.getWorld().getSceneById(sceneId); - if (scene == null) { - CommandHandler.sendMessage(null, "Scene not found in player world"); - return; - } - - scene.getEntities().values().stream() - .filter(entity -> entity instanceof EntityMonster) - .forEach(entity -> scene.killEntity(entity, 0)); - CommandHandler.sendMessage(null, "Killing all monsters in scene " + scene.getId()); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid arguments."); - } - } - } - - @Command(label = "resetconst", aliases = {"resetconstellation"}, - usage = "resetconst [all]", execution = Command.Execution.PLAYER, permission = "player.resetconstellation", - description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.") - public static class ResetConstellationCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { - player.getAvatars().forEach(this::resetConstellation); - player.dropMessage("Reset all avatars' constellations."); - } else { - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - if(entity == null) - return; - - GenshinAvatar avatar = entity.getAvatar(); - this.resetConstellation(avatar); - - player.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes."); - } - } - - private void resetConstellation(GenshinAvatar avatar) { - avatar.getTalentIdList().clear(); - avatar.setCoreProudSkillLevel(0); - avatar.recalcStats(); - avatar.save(); - } - } - - @Command(label = "godmode", - usage = "godmode", execution = Command.Execution.PLAYER, description = "Prevents you from taking damage", permission = "player.godmode") - public static class GodModeCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - player.setGodmode(!player.inGodmode()); - player.dropMessage("Godmode is now " + (player.inGodmode() ? "enabled" : "disabled") + "."); - } - } - - @Command(label = "sethealth", aliases = {"sethp"}, - usage = "sethealth ", execution = Command.Execution.PLAYER, description = "Sets your health to the specified value", - permission = "player.sethealth") - public static class SetHealthCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: sethealth "); return; - } - - try { - int health = Integer.parseInt(args.get(0)); - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - if(entity == null) - return; - - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - player.dropMessage("Health set to " + health + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid health value."); - } - } - } - - @Command(label = "setworldlevel", aliases = {"setworldlvl"}, usage = "setworldlevel ", - description = "Sets your world level (Relog to see proper effects)", permission = "player.setworldlevel", - execution = Command.Execution.PLAYER) - public static class SetWorldLevelCommand implements CommandHandler { - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: setworldlevel "); return; - } - - try { - int level = Integer.parseInt(args.get(0)); - - // Set in both world and player props - player.getWorld().setWorldLevel(level); - player.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level); - - player.dropMessage("World level set to " + level + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid world level."); - } - } - } - - @Command(label = "clearartifacts", aliases = {"clearart"}, - usage = "clearartifacts", execution = Command.Execution.PLAYER, permission = "player.clearartifacts", - description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory") - public static class ClearArtifactsCommand implements CommandHandler { - @Override - public void execute(GenshinPlayer player, List args) { - Inventory playerInventory = player.getInventory(); - playerInventory.getItems().values().stream() - .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) - .filter(item -> item.getLevel() == 1 && item.getExp() == 0) - .filter(item -> !item.isLocked() && !item.isEquipped()) - .forEach(item -> playerInventory.removeItem(item, item.getCount())); - } - } - - @Command(label = "changescene", aliases = {"scene"}, - usage = "changescene ", description = "Changes your scene", permission = "player.changescene", execution = Command.Execution.PLAYER) - public static class ChangeSceneCommand implements CommandHandler { - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: changescene "); return; - } - - try { - int sceneId = Integer.parseInt(args.get(0)); - boolean result = player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); - - if (!result) { - CommandHandler.sendMessage(null, "Scene does not exist or you are already in it"); - } - } catch (Exception e) { - CommandHandler.sendMessage(player, "Usage: changescene "); return; - } - } - } - - @Command(label = "sendservermessage", aliases = {"sendservmsg"}, - usage = "sendservermessage ", description = "Sends a message to a player as the server", - execution = Command.Execution.PLAYER, permission = "server.sendmessage") - public static class SendServerMessageCommand implements CommandHandler { - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: sendmessage "); return; - } - - try { - int target = Integer.parseInt(args.get(0)); - String message = String.join(" ", args.subList(1, args.size())); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; - } - - targetPlayer.dropMessage(message); - CommandHandler.sendMessage(null, "Message sent."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid player ID."); - } - } - } -} diff --git a/src/main/java/emu/grasscutter/commands/ServerCommands.java b/src/main/java/emu/grasscutter/commands/ServerCommands.java deleted file mode 100644 index edeac6ca3..000000000 --- a/src/main/java/emu/grasscutter/commands/ServerCommands.java +++ /dev/null @@ -1,333 +0,0 @@ -package emu.grasscutter.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.Account; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.*; - -/** - * A container for server-related commands. - */ -public final class ServerCommands { - @Command(label = "reload", usage = "reload", description = "Reload server config", permission = "server.reload") - public static class ReloadCommand implements CommandHandler { - - @Override - public void execute(List args) { - Grasscutter.getLogger().info("Reloading config."); - Grasscutter.loadConfig(); - Grasscutter.getDispatchServer().loadQueries(); - Grasscutter.getLogger().info("Reload complete."); - } - - @Override - public void execute(GenshinPlayer player, List args) { - CommandHandler.sendMessage(player, "Reloading config."); - this.execute(args); - CommandHandler.sendMessage(player, "Reload complete."); - } - } - - @Command(label = "kick", usage = "kick ", description = "Kicks the specified player from the server (WIP)", permission = "server.kick") - public static class KickCommand implements CommandHandler { - @Override - public void execute(List args) { - this.execute(null, args); - } - - @Override - public void execute(GenshinPlayer player, List args) { - int target = Integer.parseInt(args.get(0)); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); - return; - } - - if(player != null) { - CommandHandler.sendMessage(null, String.format("Player [%s:%s] has kicked player [%s:%s]", player.getAccount().getPlayerId(), player.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); - } - CommandHandler.sendMessage(player, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername())); - - targetPlayer.getSession().close(); - } - } - - @Command(label = "stop", usage = "stop", description = "Stops the server", permission = "server.stop") - public static class StopCommand implements CommandHandler { - @Override - public void execute(List args) { - Grasscutter.getLogger().info("Server shutting down..."); - for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { - p.dropMessage("Server shutting down..."); - } - - System.exit(1); - } - } - - @Command(label = "broadcast", aliases = {"b"}, - usage = "broadcast ", description = "Sends a message to all the players", permission = "server.broadcast") - public static class BroadcastCommand implements CommandHandler { - @Override - public void execute(List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: broadcast "); return; - } - - String message = String.join(" ", args.subList(0, args.size())); - - for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { - p.dropMessage(message); - } - - CommandHandler.sendMessage(null, "Message sent."); - } - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: broadcast "); return; - } - - String message = String.join(" ", args.subList(0, args.size())); - - for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { - p.dropMessage(message); - } - - CommandHandler.sendMessage(player, "Message sent."); - } - } - - @Command(label = "sendmessage", aliases = {"sendmsg", "msg"}, - usage = "sendmessage ", description = "Sends a message to a player") - public static class SendMessageCommand implements CommandHandler { - - @Override - public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: sendmessage "); return; - } - - try { - int target = Integer.parseInt(args.get(0)); - String message = String.join(" ", args.subList(1, args.size())); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; - } - - targetPlayer.dropMessage(message); - CommandHandler.sendMessage(null, "Message sent."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid player ID."); - } - } - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(player, "Usage: sendmessage "); return; - } - - try { - int target = Integer.parseInt(args.get(0)); - String message = String.join(" ", args.subList(1, args.size())); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); return; - } - - targetPlayer.sendMessage(player, message); - CommandHandler.sendMessage(player, "Message sent."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(player, "Invalid player ID."); - } - } - } - - @Command(label = "account", - usage = "account [uid]", - description = "Modify user accounts", execution = Command.Execution.CONSOLE) - public static class AccountCommand implements CommandHandler { - - @Override - public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: account [uid]"); return; - } - - String action = args.get(0); - String username = args.get(1); - - switch(action) { - default: - CommandHandler.sendMessage(null, "Usage: account [uid]"); - return; - case "create": - int uid = 0; - if(args.size() > 2) { - try { - uid = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid UID."); return; - } - } - - Account account = DatabaseHelper.createAccountWithId(username, uid); - if(account == null) { - CommandHandler.sendMessage(null, "Account already exists."); return; - } else { - CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); - account.addPermission("*"); // Grant the player superuser permissions. - } - return; - case "delete": - if(DatabaseHelper.deleteAccount(username)) { - CommandHandler.sendMessage(null, "Account deleted."); return; - } else CommandHandler.sendMessage(null, "Account not found."); - return; - } - } - } - - @Command(label = "permission", - usage = "permission ", - description = "Grants or removes a permission for a user", permission = "*") - public static class PermissionCommand implements CommandHandler { - - @Override - public void execute(List args) { - if(args.size() < 3) { - CommandHandler.sendMessage(null, "Usage: permission "); return; - } - - String action = args.get(0); - String username = args.get(1); - String permission = args.get(2); - - Account account = Grasscutter.getGameServer().getAccountByName(username); - if(account == null) { - CommandHandler.sendMessage(null, "Account not found."); return; - } - - switch(action) { - default: - CommandHandler.sendMessage(null, "Usage: permission "); - break; - case "add": - if(account.addPermission(permission)) { - CommandHandler.sendMessage(null, "Permission added."); - } else CommandHandler.sendMessage(null, "They already have this permission!"); - break; - case "remove": - if(account.removePermission(permission)) { - CommandHandler.sendMessage(null, "Permission removed."); - } else CommandHandler.sendMessage(null, "They don't have this permission!"); - break; - } - - account.save(); - } - } - - @Command(label = "help", - usage = "help [command]", description = "Sends the help message or shows information about a specified command") - public static class HelpCommand implements CommandHandler { - - @Override - public void execute(List args) { - this.execute(null, args); - } - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - HashMap handlers = CommandMap.getInstance().getHandlers(); - List annotations = new ArrayList<>(); - for(String key : handlers.keySet()) { - Command annotation = handlers.get(key).getClass().getAnnotation(Command.class); - - if(!Arrays.asList(annotation.aliases()).contains(key)) { - if(player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) continue; - annotations.add(annotation); - } - } - - - SendAllHelpMessage(player, annotations); - } else { - String command = args.get(0); - CommandHandler handler = CommandMap.getInstance().getHandler(command); - StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n"); - if(handler == null) { - builder.append("No command found."); - } else { - Command annotation = handler.getClass().getAnnotation(Command.class); - - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if(annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - if(player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) { - builder.append("\n Warning: You do not have permission to run this command."); - } - } - - CommandHandler.sendMessage(player, builder.toString()); - } - } - - void SendAllHelpMessage(GenshinPlayer player, List annotations) { - if(player == null) { - StringBuilder builder = new StringBuilder("\nAvailable commands:\n"); - annotations.forEach(annotation -> { - if (annotation.execution() != Command.Execution.PLAYER) { - builder.append(annotation.label()).append("\n"); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if (annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - - builder.append("\n"); - } - }); - - CommandHandler.sendMessage(null, builder.toString()); - } else { - CommandHandler.sendMessage(player, "Available commands:"); - annotations.forEach(annotation -> { - if (annotation.execution() != Command.Execution.CONSOLE) { - StringBuilder builder = new StringBuilder(annotation.label()).append("\n"); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if (annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - - CommandHandler.sendMessage(player, builder.toString()); - } - }); - } - } - } -} diff --git a/src/main/java/emu/grasscutter/game/managers/ChatManager.java b/src/main/java/emu/grasscutter/game/managers/ChatManager.java index 40b1a55b5..d9867e875 100644 --- a/src/main/java/emu/grasscutter/game/managers/ChatManager.java +++ b/src/main/java/emu/grasscutter/game/managers/ChatManager.java @@ -1,6 +1,6 @@ package emu.grasscutter.game.managers; -import emu.grasscutter.commands.CommandMap; +import emu.grasscutter.command.CommandMap; import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.server.game.GameServer; diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index b42ced55c..fce6823fc 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -6,7 +6,7 @@ import java.util.concurrent.ConcurrentHashMap; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; -import emu.grasscutter.commands.CommandMap; +import emu.grasscutter.command.CommandMap; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; import emu.grasscutter.game.GenshinPlayer; From b44df66c940a7581e62bc723422f59104e54523a Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Wed, 20 Apr 2022 20:56:22 +0800 Subject: [PATCH 24/91] Save account to database Signed-off-by: Jaida Wu --- src/main/java/emu/grasscutter/command/CommandMap.java | 2 +- src/main/java/emu/grasscutter/command/commands/Account.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/CommandMap.java b/src/main/java/emu/grasscutter/command/CommandMap.java index d61e46833..abc61ea30 100644 --- a/src/main/java/emu/grasscutter/command/CommandMap.java +++ b/src/main/java/emu/grasscutter/command/CommandMap.java @@ -129,7 +129,7 @@ public final class CommandMap { if (player != null) { String permissionNode = this.annotations.get(label).permission(); Account account = player.getAccount(); - if (!Objects.equals(permissionNode, "") && !account.hasPermission(permissionNode)) { + if (!permissionNode.equals("") && !account.hasPermission(permissionNode)) { CommandHandler.sendMessage(player, "You do not have permission to run this command."); return; } diff --git a/src/main/java/emu/grasscutter/command/commands/Account.java b/src/main/java/emu/grasscutter/command/commands/Account.java index 4d4a80db4..be93a7a0c 100644 --- a/src/main/java/emu/grasscutter/command/commands/Account.java +++ b/src/main/java/emu/grasscutter/command/commands/Account.java @@ -48,6 +48,7 @@ public class Account implements CommandHandler { } else { CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); account.addPermission("*"); // Grant the player superuser permissions. + account.save(); // Save account to database. } return; case "delete": From c42fa4ee5d5ef1ad25a616c1501a80e1d5c7afe7 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Wed, 20 Apr 2022 20:58:57 +0800 Subject: [PATCH 25/91] Add pos command Signed-off-by: Jaida Wu --- .../emu/grasscutter/command/commands/Pos.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/emu/grasscutter/command/commands/Pos.java diff --git a/src/main/java/emu/grasscutter/command/commands/Pos.java b/src/main/java/emu/grasscutter/command/commands/Pos.java new file mode 100644 index 000000000..e77eae1ff --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Pos.java @@ -0,0 +1,22 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "pos", + usage = "Usage: pos", description = "Get coordinates.") +public class Pos implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + sender.dropMessage(String.format("Coord: %.3f, %.3f, %.3f", sender.getPos().getX(), sender.getPos().getY(), sender.getPos().getZ())); + } +} From e8a305990745387c8c1e157bd87e97c88a5b5d05 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Wed, 20 Apr 2022 21:03:30 +0800 Subject: [PATCH 26/91] Add weather command Signed-off-by: Jaida Wu --- .../grasscutter/command/commands/Weather.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/emu/grasscutter/command/commands/Weather.java diff --git a/src/main/java/emu/grasscutter/command/commands/Weather.java b/src/main/java/emu/grasscutter/command/commands/Weather.java new file mode 100644 index 000000000..569080820 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Weather.java @@ -0,0 +1,39 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.props.ClimateType; +import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify; + +import java.util.List; + +@Command(label = "weather", usage = "weather ", + description = "Changes the weather.", aliases = {"w"}, permission = "player.weather") +public class Weather implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: weather "); + return; + } + + try { + int weatherId = Integer.parseInt(args.get(0)); + + ClimateType climate = ClimateType.getTypeByValue(weatherId); + + sender.getScene().setClimate(climate); + sender.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(sender)); + CommandHandler.sendMessage(sender, "Changed weather to " + weatherId); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid weather ID."); + } + } +} From 00a746be3ca4c73e484f12eec8e5f06f60767bd0 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Wed, 20 Apr 2022 21:04:09 +0800 Subject: [PATCH 27/91] Correct pos usage Signed-off-by: Jaida Wu --- src/main/java/emu/grasscutter/command/commands/Pos.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/Pos.java b/src/main/java/emu/grasscutter/command/commands/Pos.java index e77eae1ff..5ce495a8b 100644 --- a/src/main/java/emu/grasscutter/command/commands/Pos.java +++ b/src/main/java/emu/grasscutter/command/commands/Pos.java @@ -6,8 +6,8 @@ import emu.grasscutter.game.GenshinPlayer; import java.util.List; -@Command(label = "pos", - usage = "Usage: pos", description = "Get coordinates.") +@Command(label = "pos", usage = "pos", + description = "Get coordinates.") public class Pos implements CommandHandler { @Override From 54faab8f0d83f3de1453621e4ef5506538de69d5 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Wed, 20 Apr 2022 21:06:59 +0800 Subject: [PATCH 28/91] Add restart command Signed-off-by: Jaida Wu --- .../grasscutter/command/commands/Restart.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/emu/grasscutter/command/commands/Restart.java diff --git a/src/main/java/emu/grasscutter/command/commands/Restart.java b/src/main/java/emu/grasscutter/command/commands/Restart.java new file mode 100644 index 000000000..3457dc6c6 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Restart.java @@ -0,0 +1,16 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "restart", usage = "restart - Restarts the current session") +public class Restart implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + sender.getSession().close(); + } +} From de0a14a5899a0a3676ab2a5cedbe8d11675f4408 Mon Sep 17 00:00:00 2001 From: iTruth Date: Wed, 20 Apr 2022 21:14:18 +0800 Subject: [PATCH 29/91] Fix playerOpt is always throw an exception when it's null --- src/main/java/emu/grasscutter/server/game/GameServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index b42ced55c..b99063cb6 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -145,7 +145,7 @@ public final class GameServer extends MihoyoKcpServer { public Account getAccountByName(String username) { Optional playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst(); - if (playerOpt.get() != null) { + if (playerOpt.isPresent()) { return playerOpt.get().getAccount(); } return DatabaseHelper.getAccountByName(username); From 4d8a16ece892657dd2c84688a5ad3369c4e723f7 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Wed, 20 Apr 2022 21:15:37 +0800 Subject: [PATCH 30/91] Add setstats command Signed-off-by: Jaida Wu --- .../command/commands/SetHealth.java | 42 ------ .../command/commands/SetStats.java | 120 ++++++++++++++++++ 2 files changed, 120 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/emu/grasscutter/command/commands/SetHealth.java create mode 100644 src/main/java/emu/grasscutter/command/commands/SetStats.java diff --git a/src/main/java/emu/grasscutter/command/commands/SetHealth.java b/src/main/java/emu/grasscutter/command/commands/SetHealth.java deleted file mode 100644 index 2daf792ef..000000000 --- a/src/main/java/emu/grasscutter/command/commands/SetHealth.java +++ /dev/null @@ -1,42 +0,0 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; - -import java.util.List; - -@Command(label = "sethealth", usage = "sethealth ", - description = "Sets your health to the specified value", aliases = {"sethp"}, permission = "player.sethealth") -public class SetHealth implements CommandHandler { - - @Override - public void onCommand(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; // TODO: set player's health from console or other players - } - - if (args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: sethealth "); - return; - } - - try { - int health = Integer.parseInt(args.get(0)); - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - if (entity == null) { - return; - } - - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - sender.dropMessage("Health set to " + health + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid health value."); - } - } -} diff --git a/src/main/java/emu/grasscutter/command/commands/SetStats.java b/src/main/java/emu/grasscutter/command/commands/SetStats.java new file mode 100644 index 000000000..64f594ded --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/SetStats.java @@ -0,0 +1,120 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; + +import java.util.List; + +@Command(label = "setstats", usage = "setstats ", + aliases = {"stats"}) +public class SetStats implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + String stat = args.get(0); + switch (stat) { + default: + CommandHandler.sendMessage(sender, "Usage: setstats|stats "); + return; + case "hp": + try { + int health = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + CommandHandler.sendMessage(sender, "HP set to " + health + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid HP value."); + return; + } + break; + case "def": + try { + int def = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE)); + CommandHandler.sendMessage(sender, "DEF set to " + def + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid DEF value."); + return; + } + break; + case "atk": + try { + int atk = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_ATTACK)); + CommandHandler.sendMessage(sender, "ATK set to " + atk + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid ATK value."); + return; + } + break; + case "em": + try { + int em = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_ELEMENT_MASTERY, em); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ELEMENT_MASTERY)); + CommandHandler.sendMessage(sender, "Elemental Mastery set to " + em + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid EM value."); + return; + } + break; + case "er": + try { + float er = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float erecharge = er / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, erecharge); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY)); + float iger = erecharge * 100; + CommandHandler.sendMessage(sender, "Energy recharge set to " + iger + "%."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid ER value."); + return; + } + break; + case "crate": + try { + float cr = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float crate = cr / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL, crate); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL)); + float igcrate = crate * 100; + CommandHandler.sendMessage(sender, "Crit Rate set to " + igcrate + "%."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Crit Rate value."); + return; + } + break; + case "cdmg": + try { + float cdmg = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float cdamage = cdmg / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, cdamage); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL_HURT)); + float igcdmg = cdamage * 100; + CommandHandler.sendMessage(sender, "Crit DMG set to " + igcdmg + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Crit DMG value."); + return; + } + break; + } + } +} From d5d9056452bca4ab40549d50a16303b3ee95c370 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Wed, 20 Apr 2022 17:55:33 +0200 Subject: [PATCH 31/91] Custom Y position on MarkMapReq --- .../server/packet/recv/HandlerMarkMapReq.java | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java index 024728789..60d58e75e 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java @@ -15,26 +15,41 @@ import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; @Opcodes(PacketOpcodes.MarkMapReq) public class HandlerMarkMapReq extends PacketHandler { - + + private static boolean isInt(String str) { + + try { + @SuppressWarnings("unused") + int x = Integer.parseInt(str); + return true; // String is an Integer + } catch (NumberFormatException e) { + return false; // String is not an Integer + } + + } + @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { MarkMapReq req = MarkMapReq.parseFrom(payload); - + if (req.getOp() != Operation.Add) { return; } session.getPlayer().getPos().setX(req.getMark().getPos().getX()); session.getPlayer().getPos().setZ(req.getMark().getPos().getZ()); - session.getPlayer().getPos().setY(300); - - Grasscutter.getLogger().info("Player [" + session.getPlayer().getUid() + ":" + session.getPlayer().getNickname() + "] tp to " + session.getPlayer().getPos() + " Scene id: " + req.getMark().getSceneId()); - + + session.getPlayer().getPos() + .setY(isInt(req.getMark().getName()) ? Integer.parseInt(req.getMark().getName()) : 300); + + Grasscutter.getLogger().info("Player [" + session.getPlayer().getUid() + ":" + session.getPlayer().getNickname() + + "] tp to " + session.getPlayer().getPos() + " Scene id: " + req.getMark().getSceneId()); + if (req.getMark().getSceneId() != session.getPlayer().getSceneId()) { - session.getPlayer().getWorld().transferPlayerToScene(session.getPlayer(), req.getMark().getSceneId(), session.getPlayer().getPos()); + session.getPlayer().getWorld().transferPlayerToScene(session.getPlayer(), req.getMark().getSceneId(), + session.getPlayer().getPos()); } else { session.getPlayer().getScene().broadcastPacket(new PacketSceneEntityAppearNotify(session.getPlayer())); } } - } From 482c174ee7f49c2dc13bb80bcd38fd0ab8a57aaf Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 00:28:14 +0800 Subject: [PATCH 32/91] Reload command now reloads gacha config (again) --- src/main/java/emu/grasscutter/command/commands/Reload.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/emu/grasscutter/command/commands/Reload.java b/src/main/java/emu/grasscutter/command/commands/Reload.java index 21bf63e52..cf2bae2b1 100644 --- a/src/main/java/emu/grasscutter/command/commands/Reload.java +++ b/src/main/java/emu/grasscutter/command/commands/Reload.java @@ -15,6 +15,7 @@ public class Reload implements CommandHandler { public void execute(GenshinPlayer sender, List args) { CommandHandler.sendMessage(sender, "Reloading config."); Grasscutter.loadConfig(); + Grasscutter.getGameServer().getGachaManager().load(); Grasscutter.getDispatchServer().loadQueries(); CommandHandler.sendMessage(sender, "Reload complete."); } From d0529641b39e35ad4d743970d7567b5482e9af2b Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Thu, 21 Apr 2022 03:43:57 +0700 Subject: [PATCH 33/91] Adding Elemental DMG Bonus Added Elemental DMG Bonus command. > /stats --- .../command/commands/SetStats.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/main/java/emu/grasscutter/command/commands/SetStats.java b/src/main/java/emu/grasscutter/command/commands/SetStats.java index f8cac4bf4..ddc8b3aab 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStats.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStats.java @@ -115,6 +115,119 @@ public class SetStats implements CommandHandler { return; } break; + case "epyro": + try { + float epyro = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float pyro = epyro / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_FIRE_ADD_HURT, pyro); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_FIRE_ADD_HURT)); + float igpyro = pyro * 100; + CommandHandler.sendMessage(sender, "Pyro DMG Bonus set to " + igpyro + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Pyro DMG Bonus value."); + return; + } + break; + case "ecryo": + try { + float ecryo = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float cryo = ecryo / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_ICE_ADD_HURT, cryo); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ICE_ADD_HURT)); + float igcyro = cryo * 100; + CommandHandler.sendMessage(sender, "Cyro DMG Bonus set to " + igcyro + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Cryo DMG Bonus value."); + return; + } + break; + case "ehydro": + try { + float ehydro = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float hydro = ehydro / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_WATER_ADD_HURT, hydro); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_WATER_ADD_HURT)); + float ighydro = hydro * 100; + CommandHandler.sendMessage(sender, "Hydro DMG Bonus set to " + ighydro + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Hydro DMG Bonus value."); + return; + } + break; + case "eanemo": + try { + float eanemo = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float anemo = eanemo / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_WIND_ADD_HURT, anemo); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_WIND_ADD_HURT)); + float iganemo = anemo * 100; + CommandHandler.sendMessage(sender, "Anemo DMG Bonus set to " + iganemo + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Anemo DMG Bonus value."); + return; + } + break; + case "egeo": + try { + float egeo = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float geo = egeo / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_ROCK_ADD_HURT, geo); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ROCK_ADD_HURT)); + float iggeo = geo * 100; + CommandHandler.sendMessage(sender, "Geo DMG Bonus set to " + iggeo + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Geo DMG Bonus value."); + return; + } + break; + case "ethunder": + case "eelec": + try { + float eelec = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float elec = eelec / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, elec); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL_HURT)); + float igelec = elec * 100; + CommandHandler.sendMessage(sender, "Electro DMG Bonus set to " + igelec + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Electro DMG Bonus value."); + return; + } + break; + case "ephys": + try { + float ephys = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float phys = ephys / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, phys); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT)); + float igphys = phys * 100; + CommandHandler.sendMessage(sender, "Physical DMG Bonus set to " + igphys + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Physical DMG Bonus value."); + return; + } + break; + case "edend": + try { + float edend = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float dend = edend / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_GRASS_ADD_HURT, dend); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_GRASS_ADD_HURT)); + float igdend = dend * 100; + CommandHandler.sendMessage(sender, "Dendro DMG Bonus set to " + igdend + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Dendro DMG Bonus value."); + return; + } + break; } } } From 6f43c0765fe0a8eb01aba30c26d707fe3b1a7a2c Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Thu, 21 Apr 2022 04:45:38 +0800 Subject: [PATCH 34/91] teleports --- .../emu/grasscutter/data/GenshinData.java | 6 +++ .../emu/grasscutter/data/ResourceLoader.java | 44 +++++++++++++++++++ .../grasscutter/data/common/PointData.java | 43 ++++++++++++++++++ .../data/common/ScenePointConfig.java | 15 +++++++ .../data/custom/ScenePointEntry.java | 21 +++++++++ src/main/java/emu/grasscutter/game/World.java | 19 ++++++++ .../recv/HandlerSceneTransToPointReq.java | 18 ++++++++ .../send/PacketSceneTransToPointRsp.java | 35 +++++++++++++++ 8 files changed, 201 insertions(+) create mode 100644 src/main/java/emu/grasscutter/data/common/PointData.java create mode 100644 src/main/java/emu/grasscutter/data/common/ScenePointConfig.java create mode 100644 src/main/java/emu/grasscutter/data/custom/ScenePointEntry.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java diff --git a/src/main/java/emu/grasscutter/data/GenshinData.java b/src/main/java/emu/grasscutter/data/GenshinData.java index bb2709108..7dc5a5f7c 100644 --- a/src/main/java/emu/grasscutter/data/GenshinData.java +++ b/src/main/java/emu/grasscutter/data/GenshinData.java @@ -8,6 +8,7 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.utils.Utils; import emu.grasscutter.data.custom.AbilityEmbryoEntry; import emu.grasscutter.data.custom.OpenConfigEntry; +import emu.grasscutter.data.custom.ScenePointEntry; import emu.grasscutter.data.def.*; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -18,6 +19,7 @@ public class GenshinData { private static final Int2ObjectMap abilityHashes = new Int2ObjectOpenHashMap<>(); private static final Map abilityEmbryos = new HashMap<>(); private static final Map openConfigEntries = new HashMap<>(); + private static final Map scenePointEntries = new HashMap<>(); // ExcelConfigs private static final Int2ObjectMap playerLevelDataMap = new Int2ObjectOpenHashMap<>(); @@ -82,6 +84,10 @@ public class GenshinData { return openConfigEntries; } + public static Map getScenePointEntries() { + return scenePointEntries; + } + public static Int2ObjectMap getAvatarDataMap() { return avatarDataMap; } diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index f50067c65..e597257ce 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -10,11 +10,15 @@ import java.util.regex.Pattern; import emu.grasscutter.utils.Utils; import org.reflections.Reflections; +import com.google.gson.JsonElement; import com.google.gson.reflect.TypeToken; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.common.PointData; +import emu.grasscutter.data.common.ScenePointConfig; import emu.grasscutter.data.custom.AbilityEmbryoEntry; import emu.grasscutter.data.custom.OpenConfigEntry; +import emu.grasscutter.data.custom.ScenePointEntry; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; public class ResourceLoader { @@ -42,6 +46,7 @@ public class ResourceLoader { loadOpenConfig(); // Load resources loadResources(); + loadScenePoints(); // Process into depots GenshinDepot.load(); // Custom - TODO move this somewhere else @@ -121,6 +126,45 @@ public class ResourceLoader { } } + private static void loadScenePoints() { + Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)"); + File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutPut/Scene/Point"); + List scenePointList = new ArrayList<>(); + for (File file : folder.listFiles()) { + ScenePointConfig config = null; + Integer sceneId = null; + + Matcher matcher = pattern.matcher(file.getName()); + if (matcher.find()) { + sceneId = Integer.parseInt(matcher.group(1)); + } else { + continue; + } + + try (FileReader fileReader = new FileReader(file)) { + config = Grasscutter.getGsonFactory().fromJson(fileReader, ScenePointConfig.class); + } catch (Exception e) { + e.printStackTrace(); + continue; + } + + if (config.points == null) { + continue; + } + + for (Map.Entry entry : config.points.entrySet()) { + PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class); + + ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData); + scenePointList.add(sl); + } + + for (ScenePointEntry entry : scenePointList) { + GenshinData.getScenePointEntries().put(entry.getName(), entry); + } + } + } + private static void loadAbilityEmbryos() { // Read from cached file if exists File embryoCache = new File(Grasscutter.getConfig().DATA_FOLDER + "AbilityEmbryos.json"); diff --git a/src/main/java/emu/grasscutter/data/common/PointData.java b/src/main/java/emu/grasscutter/data/common/PointData.java new file mode 100644 index 000000000..7c31d2f06 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/PointData.java @@ -0,0 +1,43 @@ +package emu.grasscutter.data.common; + +public class PointData { + private pos tranPos; + + public pos getTranPos() { + return tranPos; + } + + public void setTranPos(pos tranPos) { + this.tranPos = tranPos; + } + + public class pos { + private float x; + private float y; + private float z; + + public float getX() { + return x; + } + + public void setX(float x) { + this.x = x; + } + + public float getY() { + return y; + } + + public void setY(float y) { + this.y = y; + } + + public float getZ() { + return z; + } + + public void setZ(float z) { + this.z = z; + } + } +} diff --git a/src/main/java/emu/grasscutter/data/common/ScenePointConfig.java b/src/main/java/emu/grasscutter/data/common/ScenePointConfig.java new file mode 100644 index 000000000..340f7476f --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/ScenePointConfig.java @@ -0,0 +1,15 @@ +package emu.grasscutter.data.common; + +import com.google.gson.JsonObject; + +public class ScenePointConfig { + public JsonObject points; + + public JsonObject getPoints() { + return points; + } + + public void setPoints(JsonObject Points) { + points = Points; + } +} diff --git a/src/main/java/emu/grasscutter/data/custom/ScenePointEntry.java b/src/main/java/emu/grasscutter/data/custom/ScenePointEntry.java new file mode 100644 index 000000000..3e904f5c8 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/custom/ScenePointEntry.java @@ -0,0 +1,21 @@ +package emu.grasscutter.data.custom; + +import emu.grasscutter.data.common.PointData; + +public class ScenePointEntry { + private String name; + private PointData pointData; + + public ScenePointEntry(String name, PointData pointData) { + this.name = name; + this.pointData = pointData; + } + + public String getName() { + return name; + } + + public PointData getPointData() { + return pointData; + } +} diff --git a/src/main/java/emu/grasscutter/game/World.java b/src/main/java/emu/grasscutter/game/World.java index 02c0df63a..4375d8e4f 100644 --- a/src/main/java/emu/grasscutter/game/World.java +++ b/src/main/java/emu/grasscutter/game/World.java @@ -206,6 +206,25 @@ public class World implements Iterable { public void deregisterScene(GenshinScene scene) { this.getScenes().remove(scene.getId()); } + + public boolean forceTransferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { + // Forces the client to reload the scene map to prevent the player from falling off the map. + if (GenshinData.getSceneDataMap().get(sceneId) == null) { + return false; + } + + if (player.getScene() != null) { + player.getScene().removePlayer(player); + } + + GenshinScene scene = this.getSceneById(sceneId); + scene.addPlayer(player); + player.getPos().set(pos); + + // Teleport packet + player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterSelf, EnterReason.TransPoint, sceneId, pos)); + return true; + } public boolean transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { if (player.getScene().getId() == sceneId || GenshinData.getSceneDataMap().get(sceneId) == null) { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java new file mode 100644 index 000000000..6e38fb282 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SceneTransToPointReqOuterClass.SceneTransToPointReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketSceneTransToPointRsp; + +@Opcodes(PacketOpcodes.SceneTransToPointReq) +public class HandlerSceneTransToPointReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SceneTransToPointReq req = SceneTransToPointReq.parseFrom(payload); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java new file mode 100644 index 000000000..965c6aa6b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java @@ -0,0 +1,35 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.custom.ScenePointEntry; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SceneTransToPointRspOuterClass.SceneTransToPointRsp; +import emu.grasscutter.utils.Position; + +public class PacketSceneTransToPointRsp extends GenshinPacket { + + public PacketSceneTransToPointRsp(GenshinPlayer player, int pointId, int sceneId) { + super(PacketOpcodes.SceneTransToPointRsp); + + String code = sceneId + "_" + pointId; + ScenePointEntry scenePointEntry = GenshinData.getScenePointEntries().get(code); + + float x = scenePointEntry.getPointData().getTranPos().getX(); + float y = scenePointEntry.getPointData().getTranPos().getY(); + float z = scenePointEntry.getPointData().getTranPos().getZ(); + + player.getPos().set(new Position(x, y, z)); + + player.getWorld().forceTransferPlayerToScene(player, sceneId, player.getPos()); + + SceneTransToPointRsp proto = SceneTransToPointRsp.newBuilder() + .setRetcode(0) + .setPointId(pointId) + .setSceneId(sceneId) + .build(); + + this.setData(proto); + } +} From d33ab09e5f89fdab99122bbd4c57d8169e99fcad Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Thu, 21 Apr 2022 03:47:17 +0700 Subject: [PATCH 35/91] Added Elemental Bonus Usage: /stats for elemental bonus --- src/main/java/emu/grasscutter/command/commands/SetStats.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/SetStats.java b/src/main/java/emu/grasscutter/command/commands/SetStats.java index ddc8b3aab..4ddd60fc0 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStats.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStats.java @@ -9,7 +9,7 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import java.util.List; -@Command(label = "setstats", usage = "setstats ", +@Command(label = "setstats", usage = "setstats|stats ", aliases = {"stats"}) public class SetStats implements CommandHandler { @@ -23,7 +23,8 @@ public class SetStats implements CommandHandler { String stat = args.get(0); switch (stat) { default: - CommandHandler.sendMessage(sender, "Usage: setstats|stats "); + CommandHandler.sendMessage(sender, "Usage: /setstats|stats for basic stats"); + CommandHandler.sendMessage(sender, "Usage: /stats for elemental bonus"); return; case "hp": try { From d470cf76d85700bec1b3bcd4e752a656213181eb Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Thu, 21 Apr 2022 04:51:45 +0800 Subject: [PATCH 36/91] rsp sorry From ec438dc16ed561b7af42d0a3c379734ed5860ccd Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Thu, 21 Apr 2022 04:59:42 +0800 Subject: [PATCH 37/91] session send sorry --- .../server/packet/recv/HandlerSceneTransToPointReq.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java index 6e38fb282..619a37b1e 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java @@ -13,6 +13,7 @@ public class HandlerSceneTransToPointReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { SceneTransToPointReq req = SceneTransToPointReq.parseFrom(payload); + session.send(new PacketSceneTransToPointRsp(session.getPlayer(), req.getPointId(), req.getSceneId())); } } From 652a2154cefb6f32848250b111240c301e86bba9 Mon Sep 17 00:00:00 2001 From: KingRainbow44 Date: Wed, 20 Apr 2022 18:48:24 -0400 Subject: [PATCH 38/91] Magix sanity. --- .../{Account.java => AccountCommand.java} | 124 ++++----- .../{Broadcast.java => BroadcastCommand.java} | 58 ++--- ...angeScene.java => ChangeSceneCommand.java} | 72 +++--- ...ifacts.java => ClearArtifactsCommand.java} | 60 ++--- .../commands/{Drop.java => DropCommand.java} | 110 ++++---- .../{GiveChar.java => GiveCharCommand.java} | 186 +++++++------- .../commands/{Give.java => GiveCommand.java} | 224 ++++++++-------- .../{GodMode.java => GodModeCommand.java} | 44 ++-- .../commands/{Help.java => HelpCommand.java} | 182 ++++++------- .../commands/{Kick.java => KickCommand.java} | 62 ++--- .../{KillAll.java => KillAllCommand.java} | 128 +++++----- ...Permission.java => PermissionCommand.java} | 98 +++---- .../{Pos.java => PositionCommand.java} | 44 ++-- .../{Reload.java => ReloadCommand.java} | 44 ++-- ...ResetConst.java => ResetConstCommand.java} | 88 +++---- .../{Restart.java => RestartCommand.java} | 32 +-- ...ndMessage.java => SendMessageCommand.java} | 72 +++--- .../{SetStats.java => SetStatsCommand.java} | 240 +++++++++--------- ...ldLevel.java => SetWorldLevelCommand.java} | 78 +++--- .../{Spawn.java => SpawnCommand.java} | 102 ++++---- .../commands/{Stop.java => StopCommand.java} | 46 ++-- .../{Weather.java => WeatherCommand.java} | 78 +++--- 22 files changed, 1086 insertions(+), 1086 deletions(-) rename src/main/java/emu/grasscutter/command/commands/{Account.java => AccountCommand.java} (95%) rename src/main/java/emu/grasscutter/command/commands/{Broadcast.java => BroadcastCommand.java} (91%) rename src/main/java/emu/grasscutter/command/commands/{ChangeScene.java => ChangeSceneCommand.java} (93%) rename src/main/java/emu/grasscutter/command/commands/{ClearArtifacts.java => ClearArtifactsCommand.java} (93%) rename src/main/java/emu/grasscutter/command/commands/{Drop.java => DropCommand.java} (95%) rename src/main/java/emu/grasscutter/command/commands/{GiveChar.java => GiveCharCommand.java} (96%) rename src/main/java/emu/grasscutter/command/commands/{Give.java => GiveCommand.java} (96%) rename src/main/java/emu/grasscutter/command/commands/{GodMode.java => GodModeCommand.java} (91%) rename src/main/java/emu/grasscutter/command/commands/{Help.java => HelpCommand.java} (96%) rename src/main/java/emu/grasscutter/command/commands/{Kick.java => KickCommand.java} (93%) rename src/main/java/emu/grasscutter/command/commands/{KillAll.java => KillAllCommand.java} (96%) rename src/main/java/emu/grasscutter/command/commands/{Permission.java => PermissionCommand.java} (94%) rename src/main/java/emu/grasscutter/command/commands/{Pos.java => PositionCommand.java} (82%) rename src/main/java/emu/grasscutter/command/commands/{Reload.java => ReloadCommand.java} (91%) rename src/main/java/emu/grasscutter/command/commands/{ResetConst.java => ResetConstCommand.java} (94%) rename src/main/java/emu/grasscutter/command/commands/{Restart.java => RestartCommand.java} (86%) rename src/main/java/emu/grasscutter/command/commands/{SendMessage.java => SendMessageCommand.java} (93%) rename src/main/java/emu/grasscutter/command/commands/{SetStats.java => SetStatsCommand.java} (97%) rename src/main/java/emu/grasscutter/command/commands/{SetWorldLevel.java => SetWorldLevelCommand.java} (93%) rename src/main/java/emu/grasscutter/command/commands/{Spawn.java => SpawnCommand.java} (95%) rename src/main/java/emu/grasscutter/command/commands/{Stop.java => StopCommand.java} (90%) rename src/main/java/emu/grasscutter/command/commands/{Weather.java => WeatherCommand.java} (93%) diff --git a/src/main/java/emu/grasscutter/command/commands/Account.java b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java similarity index 95% rename from src/main/java/emu/grasscutter/command/commands/Account.java rename to src/main/java/emu/grasscutter/command/commands/AccountCommand.java index cd2978feb..2c21cba79 100644 --- a/src/main/java/emu/grasscutter/command/commands/Account.java +++ b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java @@ -1,62 +1,62 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "account", usage = "account [uid]", - description = "Modify user accounts") -public class Account implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender != null) { - CommandHandler.sendMessage(sender, "This command can only be run from the console."); - return; - } - - if (args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: account [uid]"); - return; - } - - String action = args.get(0); - String username = args.get(1); - - switch (action) { - default: - CommandHandler.sendMessage(null, "Usage: account [uid]"); - return; - case "create": - int uid = 0; - if (args.size() > 2) { - try { - uid = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid UID."); - return; - } - } - - emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, uid); - if (account == null) { - CommandHandler.sendMessage(null, "Account already exists."); - return; - } else { - CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); - account.addPermission("*"); // Grant the player superuser permissions. - account.save(); // Save account to database. - } - return; - case "delete": - if (DatabaseHelper.deleteAccount(username)) { - CommandHandler.sendMessage(null, "Account deleted."); - } else { - CommandHandler.sendMessage(null, "Account not found."); - } - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "account", usage = "account [uid]", + description = "Modify user accounts") +public final class AccountCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender != null) { + CommandHandler.sendMessage(sender, "This command can only be run from the console."); + return; + } + + if (args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: account [uid]"); + return; + } + + String action = args.get(0); + String username = args.get(1); + + switch (action) { + default: + CommandHandler.sendMessage(null, "Usage: account [uid]"); + return; + case "create": + int uid = 0; + if (args.size() > 2) { + try { + uid = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid UID."); + return; + } + } + + emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, uid); + if (account == null) { + CommandHandler.sendMessage(null, "Account already exists."); + return; + } else { + CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); + account.addPermission("*"); // Grant the player superuser permissions. + account.save(); // Save account to database. + } + return; + case "delete": + if (DatabaseHelper.deleteAccount(username)) { + CommandHandler.sendMessage(null, "Account deleted."); + } else { + CommandHandler.sendMessage(null, "Account not found."); + } + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Broadcast.java b/src/main/java/emu/grasscutter/command/commands/BroadcastCommand.java similarity index 91% rename from src/main/java/emu/grasscutter/command/commands/Broadcast.java rename to src/main/java/emu/grasscutter/command/commands/BroadcastCommand.java index 7659fed9e..4922f16f8 100644 --- a/src/main/java/emu/grasscutter/command/commands/Broadcast.java +++ b/src/main/java/emu/grasscutter/command/commands/BroadcastCommand.java @@ -1,29 +1,29 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "broadcast", usage = "broadcast ", - description = "Sends a message to all the players", aliases = {"b"}, permission = "server.broadcast") -public class Broadcast implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (args.size() < 1) { - CommandHandler.sendMessage(sender, "Usage: broadcast "); - return; - } - - String message = String.join(" ", args.subList(0, args.size())); - - for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { - CommandHandler.sendMessage(p, message); - } - - CommandHandler.sendMessage(sender, "Message sent."); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "broadcast", usage = "broadcast ", + description = "Sends a message to all the players", aliases = {"b"}, permission = "server.broadcast") +public final class BroadcastCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: broadcast "); + return; + } + + String message = String.join(" ", args.subList(0, args.size())); + + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + CommandHandler.sendMessage(p, message); + } + + CommandHandler.sendMessage(sender, "Message sent."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ChangeScene.java b/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java similarity index 93% rename from src/main/java/emu/grasscutter/command/commands/ChangeScene.java rename to src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java index 196c7fea7..a5afe7bf7 100644 --- a/src/main/java/emu/grasscutter/command/commands/ChangeScene.java +++ b/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java @@ -1,36 +1,36 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "changescene", usage = "changescene ", - description = "Changes your scene", aliases = {"scene"}, permission = "player.changescene") -public class ChangeScene implements CommandHandler { - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; - } - - if (args.size() < 1) { - CommandHandler.sendMessage(sender, "Usage: changescene "); - return; - } - - try { - int sceneId = Integer.parseInt(args.get(0)); - boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, sender.getPos()); - - CommandHandler.sendMessage(sender, "Changed to scene " + sceneId); - if (!result) { - CommandHandler.sendMessage(sender, "Scene does not exist or you are already in it"); - } - } catch (Exception e) { - CommandHandler.sendMessage(sender, "Usage: changescene "); - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "changescene", usage = "changescene ", + description = "Changes your scene", aliases = {"scene"}, permission = "player.changescene") +public final class ChangeSceneCommand implements CommandHandler { + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: changescene "); + return; + } + + try { + int sceneId = Integer.parseInt(args.get(0)); + boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, sender.getPos()); + + CommandHandler.sendMessage(sender, "Changed to scene " + sceneId); + if (!result) { + CommandHandler.sendMessage(sender, "Scene does not exist or you are already in it"); + } + } catch (Exception e) { + CommandHandler.sendMessage(sender, "Usage: changescene "); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java b/src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java similarity index 93% rename from src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java rename to src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java index 8a46760dd..258e1e1d5 100644 --- a/src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java +++ b/src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java @@ -1,30 +1,30 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.inventory.Inventory; -import emu.grasscutter.game.inventory.ItemType; - -import java.util.List; - -@Command(label = "clearartifacts", usage = "clearartifacts", - description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory", - aliases = {"clearart"}, permission = "player.clearartifacts") -public class ClearArtifacts implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; // TODO: clear player's artifacts from console or other players - } - - Inventory playerInventory = sender.getInventory(); - playerInventory.getItems().values().stream() - .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) - .filter(item -> item.getLevel() == 1 && item.getExp() == 0) - .filter(item -> !item.isLocked() && !item.isEquipped()) - .forEach(item -> playerInventory.removeItem(item, item.getCount())); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.inventory.Inventory; +import emu.grasscutter.game.inventory.ItemType; + +import java.util.List; + +@Command(label = "clearartifacts", usage = "clearartifacts", + description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory", + aliases = {"clearart"}, permission = "player.clearartifacts") +public final class ClearArtifactsCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: clear player's artifacts from console or other players + } + + Inventory playerInventory = sender.getInventory(); + playerInventory.getItems().values().stream() + .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) + .filter(item -> item.getLevel() == 1 && item.getExp() == 0) + .filter(item -> !item.isLocked() && !item.isEquipped()) + .forEach(item -> playerInventory.removeItem(item, item.getCount())); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Drop.java b/src/main/java/emu/grasscutter/command/commands/DropCommand.java similarity index 95% rename from src/main/java/emu/grasscutter/command/commands/Drop.java rename to src/main/java/emu/grasscutter/command/commands/DropCommand.java index 21db96881..e2d7644a0 100644 --- a/src/main/java/emu/grasscutter/command/commands/Drop.java +++ b/src/main/java/emu/grasscutter/command/commands/DropCommand.java @@ -1,56 +1,56 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.entity.EntityItem; -import emu.grasscutter.utils.Position; - -import java.util.List; - -@Command(label = "drop", usage = "drop [amount]", - description = "Drops an item near you", aliases = {"d", "dropitem"}, permission = "server.drop") -public class Drop implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; - } - - if (args.size() < 1) { - CommandHandler.sendMessage(sender, "Usage: drop [amount]"); - return; - } - - try { - int item = Integer.parseInt(args.get(0)); - int amount = 1; - if (args.size() > 1) amount = Integer.parseInt(args.get(1)); - - ItemData itemData = GenshinData.getItemDataMap().get(item); - if (itemData == null) { - CommandHandler.sendMessage(sender, "Invalid item id."); - return; - } - - if (itemData.isEquip()) { - float range = (5f + (.1f * amount)); - for (int i = 0; i < amount; i++) { - Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, pos, 1); - sender.getScene().addEntity(entity); - } - } else { - EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, sender.getPos().clone().addY(3f), amount); - sender.getScene().addEntity(entity); - } - CommandHandler.sendMessage(sender, String.format("Dropped %s of %s.", amount, item)); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid item or player ID."); - } - } +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.utils.Position; + +import java.util.List; + +@Command(label = "drop", usage = "drop [amount]", + description = "Drops an item near you", aliases = {"d", "dropitem"}, permission = "server.drop") +public final class DropCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: drop [amount]"); + return; + } + + try { + int item = Integer.parseInt(args.get(0)); + int amount = 1; + if (args.size() > 1) amount = Integer.parseInt(args.get(1)); + + ItemData itemData = GenshinData.getItemDataMap().get(item); + if (itemData == null) { + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + + if (itemData.isEquip()) { + float range = (5f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, pos, 1); + sender.getScene().addEntity(entity); + } + } else { + EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, sender.getPos().clone().addY(3f), amount); + sender.getScene().addEntity(entity); + } + CommandHandler.sendMessage(sender, String.format("Dropped %s of %s.", amount, item)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + } + } } \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/GiveChar.java b/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java similarity index 96% rename from src/main/java/emu/grasscutter/command/commands/GiveChar.java rename to src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java index 9c9d87fb5..bfe0f51c0 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveChar.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java @@ -1,93 +1,93 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.avatar.GenshinAvatar; - -import java.util.List; - -@Command(label = "givechar", usage = "givechar [level]", - description = "Gives the player a specified character", aliases = {"givec"}, permission = "player.givechar") -public class GiveChar implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - int target, avatarId, level = 1, ascension; - - if (sender == null && args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: givechar [amount]"); - return; - } - - switch (args.size()) { - default: - CommandHandler.sendMessage(sender, "Usage: givechar [level]"); - return; - case 2: - try { - target = Integer.parseInt(args.get(0)); - if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { - target = sender.getUid(); - level = Integer.parseInt(args.get(1)); - avatarId = Integer.parseInt(args.get(0)); - } else { - avatarId = Integer.parseInt(args.get(1)); - } - } catch (NumberFormatException ignored) { - // TODO: Parse from avatar name using GM Handbook. - CommandHandler.sendMessage(sender, "Invalid avatar or player ID."); - return; - } - break; - case 3: - try { - target = Integer.parseInt(args.get(0)); - if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { - CommandHandler.sendMessage(sender, "Invalid player ID."); - return; - } - - avatarId = Integer.parseInt(args.get(1)); - level = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - // TODO: Parse from avatar name using GM Handbook. - CommandHandler.sendMessage(sender, "Invalid avatar or player ID."); - return; - } - break; - } - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, "Player not found."); - return; - } - - AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId); - if (avatarData == null) { - CommandHandler.sendMessage(sender, "Invalid avatar id."); - return; - } - - // Calculate ascension level. - if (level <= 40) { - ascension = (int) Math.ceil(level / 20f); - } else { - ascension = (int) Math.ceil(level / 10f) - 3; - } - - GenshinAvatar avatar = new GenshinAvatar(avatarId); - avatar.setLevel(level); - avatar.setPromoteLevel(ascension); - - // This will handle stats and talents - avatar.recalcStats(); - - targetPlayer.addAvatar(avatar); - CommandHandler.sendMessage(sender, String.format("Given %s to %s.", avatarId, target)); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.AvatarData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.avatar.GenshinAvatar; + +import java.util.List; + +@Command(label = "givechar", usage = "givechar [level]", + description = "Gives the player a specified character", aliases = {"givec"}, permission = "player.givechar") +public final class GiveCharCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + int target, avatarId, level = 1, ascension; + + if (sender == null && args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: givechar [amount]"); + return; + } + + switch (args.size()) { + default: + CommandHandler.sendMessage(sender, "Usage: givechar [level]"); + return; + case 2: + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { + target = sender.getUid(); + level = Integer.parseInt(args.get(1)); + avatarId = Integer.parseInt(args.get(0)); + } else { + avatarId = Integer.parseInt(args.get(1)); + } + } catch (NumberFormatException ignored) { + // TODO: Parse from avatar name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid avatar or player ID."); + return; + } + break; + case 3: + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + return; + } + + avatarId = Integer.parseInt(args.get(1)); + level = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + // TODO: Parse from avatar name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid avatar or player ID."); + return; + } + break; + } + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId); + if (avatarData == null) { + CommandHandler.sendMessage(sender, "Invalid avatar id."); + return; + } + + // Calculate ascension level. + if (level <= 40) { + ascension = (int) Math.ceil(level / 20f); + } else { + ascension = (int) Math.ceil(level / 10f) - 3; + } + + GenshinAvatar avatar = new GenshinAvatar(avatarId); + avatar.setLevel(level); + avatar.setPromoteLevel(ascension); + + // This will handle stats and talents + avatar.recalcStats(); + + targetPlayer.addAvatar(avatar); + CommandHandler.sendMessage(sender, String.format("Given %s to %s.", avatarId, target)); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Give.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java similarity index 96% rename from src/main/java/emu/grasscutter/command/commands/Give.java rename to src/main/java/emu/grasscutter/command/commands/GiveCommand.java index 47c015cf0..7d15a4759 100644 --- a/src/main/java/emu/grasscutter/command/commands/Give.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -1,112 +1,112 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.inventory.GenshinItem; -import emu.grasscutter.game.props.ActionReason; -import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; - -import java.util.LinkedList; -import java.util.List; - -@Command(label = "give", usage = "give [player] [amount]", - description = "Gives an item to you or the specified player", aliases = {"g", "item", "giveitem"}, permission = "player.give") -public class Give implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - int target, item, amount = 1; - - if (sender == null && args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: give [amount]"); - return; - } - - switch (args.size()) { - default: // *No args* - CommandHandler.sendMessage(sender, "Usage: give [player] [amount]"); - return; - case 1: // - try { - item = Integer.parseInt(args.get(0)); - target = sender.getUid(); - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(sender, "Invalid item id."); - return; - } - break; - case 2: // [amount] | [player] - try { - target = Integer.parseInt(args.get(0)); - - if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { - target = sender.getUid(); - item = Integer.parseInt(args.get(0)); - amount = Integer.parseInt(args.get(1)); - } else { - item = Integer.parseInt(args.get(1)); - } - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(sender, "Invalid item or player ID."); - return; - } - break; - case 3: // [player] [amount] - try { - target = Integer.parseInt(args.get(0)); - - if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { - CommandHandler.sendMessage(sender, "Invalid player ID."); - return; - } - - item = Integer.parseInt(args.get(1)); - amount = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(sender, "Invalid item or player ID."); - return; - } - break; - } - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, "Player not found."); - return; - } - - ItemData itemData = GenshinData.getItemDataMap().get(item); - if (itemData == null) { - CommandHandler.sendMessage(sender, "Invalid item id."); - return; - } - - this.item(targetPlayer, itemData, amount); - - CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target)); - } - - private void item(GenshinPlayer player, ItemData itemData, int amount) { - GenshinItem genshinItem = new GenshinItem(itemData); - if (itemData.isEquip()) { - List items = new LinkedList<>(); - for (int i = 0; i < amount; i++) { - items.add(genshinItem); - } - player.getInventory().addItems(items); - player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); - } else { - genshinItem.setCount(amount); - player.getInventory().addItem(genshinItem); - player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop)); - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; + +import java.util.LinkedList; +import java.util.List; + +@Command(label = "give", usage = "give [player] [amount]", + description = "Gives an item to you or the specified player", aliases = {"g", "item", "giveitem"}, permission = "player.give") +public final class GiveCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + int target, item, amount = 1; + + if (sender == null && args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: give [amount]"); + return; + } + + switch (args.size()) { + default: // *No args* + CommandHandler.sendMessage(sender, "Usage: give [player] [amount]"); + return; + case 1: // + try { + item = Integer.parseInt(args.get(0)); + target = sender.getUid(); + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + break; + case 2: // [amount] | [player] + try { + target = Integer.parseInt(args.get(0)); + + if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { + target = sender.getUid(); + item = Integer.parseInt(args.get(0)); + amount = Integer.parseInt(args.get(1)); + } else { + item = Integer.parseInt(args.get(1)); + } + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + return; + } + break; + case 3: // [player] [amount] + try { + target = Integer.parseInt(args.get(0)); + + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + return; + } + + item = Integer.parseInt(args.get(1)); + amount = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + return; + } + break; + } + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + ItemData itemData = GenshinData.getItemDataMap().get(item); + if (itemData == null) { + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + + this.item(targetPlayer, itemData, amount); + + CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target)); + } + + private void item(GenshinPlayer player, ItemData itemData, int amount) { + GenshinItem genshinItem = new GenshinItem(itemData); + if (itemData.isEquip()) { + List items = new LinkedList<>(); + for (int i = 0; i < amount; i++) { + items.add(genshinItem); + } + player.getInventory().addItems(items); + player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); + } else { + genshinItem.setCount(amount); + player.getInventory().addItem(genshinItem); + player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop)); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/GodMode.java b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java similarity index 91% rename from src/main/java/emu/grasscutter/command/commands/GodMode.java rename to src/main/java/emu/grasscutter/command/commands/GodModeCommand.java index a84085d4d..e5635dc9f 100644 --- a/src/main/java/emu/grasscutter/command/commands/GodMode.java +++ b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java @@ -1,22 +1,22 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "godmode", usage = "godmode [playerId]", - description = "Prevents you from taking damage", permission = "player.godmode") -public class GodMode implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; // TODO: toggle player's godmode statue from console or other players - } - sender.setGodmode(!sender.inGodmode()); - sender.dropMessage("Godmode is now " + (sender.inGodmode() ? "enabled" : "disabled") + "."); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "godmode", usage = "godmode [playerId]", + description = "Prevents you from taking damage", permission = "player.godmode") +public final class GodModeCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: toggle player's godmode statue from console or other players + } + sender.setGodmode(!sender.inGodmode()); + sender.dropMessage("Godmode is now " + (sender.inGodmode() ? "enabled" : "disabled") + "."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Help.java b/src/main/java/emu/grasscutter/command/commands/HelpCommand.java similarity index 96% rename from src/main/java/emu/grasscutter/command/commands/Help.java rename to src/main/java/emu/grasscutter/command/commands/HelpCommand.java index 376bb8cbf..abe78e7d3 100644 --- a/src/main/java/emu/grasscutter/command/commands/Help.java +++ b/src/main/java/emu/grasscutter/command/commands/HelpCommand.java @@ -1,91 +1,91 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.command.CommandMap; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.*; - -@Command(label = "help", usage = "help [command]", - description = "Sends the help message or shows information about a specified command") -public class Help implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if (args.size() < 1) { - HashMap handlers = CommandMap.getInstance().getHandlers(); - List annotations = new ArrayList<>(); - for (String key : handlers.keySet()) { - Command annotation = handlers.get(key).getClass().getAnnotation(Command.class); - - if (!Arrays.asList(annotation.aliases()).contains(key)) { - if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) - continue; - annotations.add(annotation); - } - } - - SendAllHelpMessage(player, annotations); - } else { - String command = args.get(0); - CommandHandler handler = CommandMap.getInstance().getHandler(command); - StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n"); - if (handler == null) { - builder.append("No command found."); - } else { - Command annotation = handler.getClass().getAnnotation(Command.class); - - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if (annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) { - builder.append("\n Warning: You do not have permission to run this command."); - } - } - - CommandHandler.sendMessage(player, builder.toString()); - } - } - - void SendAllHelpMessage(GenshinPlayer player, List annotations) { - if (player == null) { - StringBuilder builder = new StringBuilder("\nAvailable commands:\n"); - annotations.forEach(annotation -> { - builder.append(annotation.label()).append("\n"); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if (annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - - builder.append("\n"); - }); - - CommandHandler.sendMessage(null, builder.toString()); - } else { - CommandHandler.sendMessage(player, "Available commands:"); - annotations.forEach(annotation -> { - StringBuilder builder = new StringBuilder(annotation.label()).append("\n"); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if (annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - - CommandHandler.sendMessage(player, builder.toString()); - }); - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.command.CommandMap; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.*; + +@Command(label = "help", usage = "help [command]", + description = "Sends the help message or shows information about a specified command") +public final class HelpCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer player, List args) { + if (args.size() < 1) { + HashMap handlers = CommandMap.getInstance().getHandlers(); + List annotations = new ArrayList<>(); + for (String key : handlers.keySet()) { + Command annotation = handlers.get(key).getClass().getAnnotation(Command.class); + + if (!Arrays.asList(annotation.aliases()).contains(key)) { + if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) + continue; + annotations.add(annotation); + } + } + + SendAllHelpMessage(player, annotations); + } else { + String command = args.get(0); + CommandHandler handler = CommandMap.getInstance().getHandler(command); + StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n"); + if (handler == null) { + builder.append("No command found."); + } else { + Command annotation = handler.getClass().getAnnotation(Command.class); + + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) { + builder.append("\n Warning: You do not have permission to run this command."); + } + } + + CommandHandler.sendMessage(player, builder.toString()); + } + } + + void SendAllHelpMessage(GenshinPlayer player, List annotations) { + if (player == null) { + StringBuilder builder = new StringBuilder("\nAvailable commands:\n"); + annotations.forEach(annotation -> { + builder.append(annotation.label()).append("\n"); + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + + builder.append("\n"); + }); + + CommandHandler.sendMessage(null, builder.toString()); + } else { + CommandHandler.sendMessage(player, "Available commands:"); + annotations.forEach(annotation -> { + StringBuilder builder = new StringBuilder(annotation.label()).append("\n"); + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + + CommandHandler.sendMessage(player, builder.toString()); + }); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Kick.java b/src/main/java/emu/grasscutter/command/commands/KickCommand.java similarity index 93% rename from src/main/java/emu/grasscutter/command/commands/Kick.java rename to src/main/java/emu/grasscutter/command/commands/KickCommand.java index 9a9c19f86..188ce1877 100644 --- a/src/main/java/emu/grasscutter/command/commands/Kick.java +++ b/src/main/java/emu/grasscutter/command/commands/KickCommand.java @@ -1,31 +1,31 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "kick", usage = "kick ", - description = "Kicks the specified player from the server (WIP)", permission = "server.kick") -public class Kick implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - int target = Integer.parseInt(args.get(0)); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, "Player not found."); - return; - } - - if (sender != null) { - CommandHandler.sendMessage(sender, String.format("Player [%s:%s] has kicked player [%s:%s]", sender.getAccount().getPlayerId(), sender.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); - } - CommandHandler.sendMessage(sender, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername())); - - targetPlayer.getSession().close(); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "kick", usage = "kick ", + description = "Kicks the specified player from the server (WIP)", permission = "server.kick") +public final class KickCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + int target = Integer.parseInt(args.get(0)); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + if (sender != null) { + CommandHandler.sendMessage(sender, String.format("Player [%s:%s] has kicked player [%s:%s]", sender.getAccount().getPlayerId(), sender.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); + } + CommandHandler.sendMessage(sender, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername())); + + targetPlayer.getSession().close(); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/KillAll.java b/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java similarity index 96% rename from src/main/java/emu/grasscutter/command/commands/KillAll.java rename to src/main/java/emu/grasscutter/command/commands/KillAllCommand.java index ba434aa89..436691b57 100644 --- a/src/main/java/emu/grasscutter/command/commands/KillAll.java +++ b/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java @@ -1,64 +1,64 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.GenshinScene; -import emu.grasscutter.game.entity.EntityMonster; - -import java.util.List; - -@Command(label = "killall", usage = "killall [playerUid] [sceneId]", - description = "Kill all entities", permission = "server.killall") -public class KillAll implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - GenshinScene scene; - GenshinPlayer genshinPlayer; - - try { - switch (args.size()) { - case 0: // *No args* - if (sender == null) { - CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); - return; - } - scene = sender.getScene(); - break; - case 1: // [playerUid] - genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0))); - if (genshinPlayer == null) { - CommandHandler.sendMessage(sender, "Player not found or offline."); - return; - } - scene = genshinPlayer.getScene(); - break; - case 2: // [playerUid] [sceneId] - genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0))); - if (genshinPlayer == null) { - CommandHandler.sendMessage(sender, "Player not found or offline."); - return; - } - GenshinScene genshinScene = sender.getWorld().getSceneById(Integer.parseInt(args.get(1))); - if (genshinScene == null) { - CommandHandler.sendMessage(sender, "Scene not found in player world"); - return; - } - scene = genshinScene; - break; - default: - CommandHandler.sendMessage(sender, "Usage: killall [playerUid] [sceneId]"); - return; - } - - scene.getEntities().values().stream() - .filter(entity -> entity instanceof EntityMonster) - .forEach(entity -> scene.killEntity(entity, 0)); - CommandHandler.sendMessage(sender, "Killing all monsters in scene " + scene.getId()); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid arguments."); - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.GenshinScene; +import emu.grasscutter.game.entity.EntityMonster; + +import java.util.List; + +@Command(label = "killall", usage = "killall [playerUid] [sceneId]", + description = "Kill all entities", permission = "server.killall") +public final class KillAllCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + GenshinScene scene; + GenshinPlayer genshinPlayer; + + try { + switch (args.size()) { + case 0: // *No args* + if (sender == null) { + CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); + return; + } + scene = sender.getScene(); + break; + case 1: // [playerUid] + genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0))); + if (genshinPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found or offline."); + return; + } + scene = genshinPlayer.getScene(); + break; + case 2: // [playerUid] [sceneId] + genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0))); + if (genshinPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found or offline."); + return; + } + GenshinScene genshinScene = sender.getWorld().getSceneById(Integer.parseInt(args.get(1))); + if (genshinScene == null) { + CommandHandler.sendMessage(sender, "Scene not found in player world"); + return; + } + scene = genshinScene; + break; + default: + CommandHandler.sendMessage(sender, "Usage: killall [playerUid] [sceneId]"); + return; + } + + scene.getEntities().values().stream() + .filter(entity -> entity instanceof EntityMonster) + .forEach(entity -> scene.killEntity(entity, 0)); + CommandHandler.sendMessage(sender, "Killing all monsters in scene " + scene.getId()); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid arguments."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Permission.java b/src/main/java/emu/grasscutter/command/commands/PermissionCommand.java similarity index 94% rename from src/main/java/emu/grasscutter/command/commands/Permission.java rename to src/main/java/emu/grasscutter/command/commands/PermissionCommand.java index 353ba81c8..7218bf2cd 100644 --- a/src/main/java/emu/grasscutter/command/commands/Permission.java +++ b/src/main/java/emu/grasscutter/command/commands/PermissionCommand.java @@ -1,50 +1,50 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.Account; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "permission", usage = "permission ", - description = "Grants or removes a permission for a user", permission = "*") -public class Permission implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (args.size() < 3) { - CommandHandler.sendMessage(sender, "Usage: permission "); - return; - } - - String action = args.get(0); - String username = args.get(1); - String permission = args.get(2); - - Account account = Grasscutter.getGameServer().getAccountByName(username); - if (account == null) { - CommandHandler.sendMessage(sender, "Account not found."); - return; - } - - switch (action) { - default: - CommandHandler.sendMessage(sender, "Usage: permission "); - break; - case "add": - if (account.addPermission(permission)) { - CommandHandler.sendMessage(sender, "Permission added."); - } else CommandHandler.sendMessage(sender, "They already have this permission!"); - break; - case "remove": - if (account.removePermission(permission)) { - CommandHandler.sendMessage(sender, "Permission removed."); - } else CommandHandler.sendMessage(sender, "They don't have this permission!"); - break; - } - - account.save(); - } +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "permission", usage = "permission ", + description = "Grants or removes a permission for a user", permission = "*") +public final class PermissionCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (args.size() < 3) { + CommandHandler.sendMessage(sender, "Usage: permission "); + return; + } + + String action = args.get(0); + String username = args.get(1); + String permission = args.get(2); + + Account account = Grasscutter.getGameServer().getAccountByName(username); + if (account == null) { + CommandHandler.sendMessage(sender, "Account not found."); + return; + } + + switch (action) { + default: + CommandHandler.sendMessage(sender, "Usage: permission "); + break; + case "add": + if (account.addPermission(permission)) { + CommandHandler.sendMessage(sender, "Permission added."); + } else CommandHandler.sendMessage(sender, "They already have this permission!"); + break; + case "remove": + if (account.removePermission(permission)) { + CommandHandler.sendMessage(sender, "Permission removed."); + } else CommandHandler.sendMessage(sender, "They don't have this permission!"); + break; + } + + account.save(); + } } \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/Pos.java b/src/main/java/emu/grasscutter/command/commands/PositionCommand.java similarity index 82% rename from src/main/java/emu/grasscutter/command/commands/Pos.java rename to src/main/java/emu/grasscutter/command/commands/PositionCommand.java index 3f1d40c9d..639301c7f 100644 --- a/src/main/java/emu/grasscutter/command/commands/Pos.java +++ b/src/main/java/emu/grasscutter/command/commands/PositionCommand.java @@ -1,22 +1,22 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "pos", usage = "pos", - description = "Get coordinates.") -public class Pos implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; - } - - sender.dropMessage(String.format("Coord: %.3f, %.3f, %.3f", sender.getPos().getX(), sender.getPos().getY(), sender.getPos().getZ())); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "position", usage = "position", aliases = {"pos"}, + description = "Get coordinates.") +public final class PositionCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + sender.dropMessage(String.format("Coord: %.3f, %.3f, %.3f", sender.getPos().getX(), sender.getPos().getY(), sender.getPos().getZ())); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Reload.java b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java similarity index 91% rename from src/main/java/emu/grasscutter/command/commands/Reload.java rename to src/main/java/emu/grasscutter/command/commands/ReloadCommand.java index cf2bae2b1..b145aaf94 100644 --- a/src/main/java/emu/grasscutter/command/commands/Reload.java +++ b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java @@ -1,22 +1,22 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "reload", usage = "reload", - description = "Reload server config", permission = "server.reload") -public class Reload implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - CommandHandler.sendMessage(sender, "Reloading config."); - Grasscutter.loadConfig(); - Grasscutter.getGameServer().getGachaManager().load(); - Grasscutter.getDispatchServer().loadQueries(); - CommandHandler.sendMessage(sender, "Reload complete."); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "reload", usage = "reload", + description = "Reload server config", permission = "server.reload") +public final class ReloadCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + CommandHandler.sendMessage(sender, "Reloading config."); + Grasscutter.loadConfig(); + Grasscutter.getGameServer().getGachaManager().load(); + Grasscutter.getDispatchServer().loadQueries(); + CommandHandler.sendMessage(sender, "Reload complete."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ResetConst.java b/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java similarity index 94% rename from src/main/java/emu/grasscutter/command/commands/ResetConst.java rename to src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java index 027e6addd..77f16f9b8 100644 --- a/src/main/java/emu/grasscutter/command/commands/ResetConst.java +++ b/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java @@ -1,45 +1,45 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.avatar.GenshinAvatar; -import emu.grasscutter.game.entity.EntityAvatar; - -import java.util.List; - -@Command(label = "resetconst", usage = "resetconst [all]", - description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.", - aliases = {"resetconstellation"}, permission = "player.resetconstellation") -public class ResetConst implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; - } - - if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { - sender.getAvatars().forEach(this::resetConstellation); - sender.dropMessage("Reset all avatars' constellations."); - } else { - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - if (entity == null) { - return; - } - - GenshinAvatar avatar = entity.getAvatar(); - this.resetConstellation(avatar); - - sender.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes."); - } - } - - private void resetConstellation(GenshinAvatar avatar) { - avatar.getTalentIdList().clear(); - avatar.setCoreProudSkillLevel(0); - avatar.recalcStats(); - avatar.save(); - } +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.avatar.GenshinAvatar; +import emu.grasscutter.game.entity.EntityAvatar; + +import java.util.List; + +@Command(label = "resetconst", usage = "resetconst [all]", + description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.", + aliases = {"resetconstellation"}, permission = "player.resetconstellation") +public final class ResetConstCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { + sender.getAvatars().forEach(this::resetConstellation); + sender.dropMessage("Reset all avatars' constellations."); + } else { + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + if (entity == null) { + return; + } + + GenshinAvatar avatar = entity.getAvatar(); + this.resetConstellation(avatar); + + sender.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes."); + } + } + + private void resetConstellation(GenshinAvatar avatar) { + avatar.getTalentIdList().clear(); + avatar.setCoreProudSkillLevel(0); + avatar.recalcStats(); + avatar.save(); + } } \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/Restart.java b/src/main/java/emu/grasscutter/command/commands/RestartCommand.java similarity index 86% rename from src/main/java/emu/grasscutter/command/commands/Restart.java rename to src/main/java/emu/grasscutter/command/commands/RestartCommand.java index a00e8af67..cf40fa9bd 100644 --- a/src/main/java/emu/grasscutter/command/commands/Restart.java +++ b/src/main/java/emu/grasscutter/command/commands/RestartCommand.java @@ -1,16 +1,16 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "restart", usage = "restart - Restarts the current session") -public class Restart implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - sender.getSession().close(); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "restart", usage = "restart - Restarts the current session") +public final class RestartCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + sender.getSession().close(); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/SendMessage.java b/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java similarity index 93% rename from src/main/java/emu/grasscutter/command/commands/SendMessage.java rename to src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java index 9da80ef91..061009c19 100644 --- a/src/main/java/emu/grasscutter/command/commands/SendMessage.java +++ b/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java @@ -1,37 +1,37 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "say", usage = "say ", description = "Sends a message to a player as the server", - aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage") -public class SendMessage implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: sendmessage "); - return; - } - - try { - int target = Integer.parseInt(args.get(0)); - String message = String.join(" ", args.subList(1, args.size())); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, "Player not found."); - return; - } - - CommandHandler.sendMessage(targetPlayer, message); - CommandHandler.sendMessage(sender, "Message sent."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid player ID."); - } - } +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "say", usage = "say ", description = "Sends a message to a player as the server", + aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage") +public final class SendMessageCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: sendmessage "); + return; + } + + try { + int target = Integer.parseInt(args.get(0)); + String message = String.join(" ", args.subList(1, args.size())); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + CommandHandler.sendMessage(targetPlayer, message); + CommandHandler.sendMessage(sender, "Message sent."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + } + } } \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/SetStats.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java similarity index 97% rename from src/main/java/emu/grasscutter/command/commands/SetStats.java rename to src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java index f8cac4bf4..30e8c9f12 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStats.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java @@ -1,120 +1,120 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; - -import java.util.List; - -@Command(label = "setstats", usage = "setstats ", - aliases = {"stats"}) -public class SetStats implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; - } - - String stat = args.get(0); - switch (stat) { - default: - CommandHandler.sendMessage(sender, "Usage: setstats|stats "); - return; - case "hp": - try { - int health = Integer.parseInt(args.get(1)); - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - CommandHandler.sendMessage(sender, "HP set to " + health + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid HP value."); - return; - } - break; - case "def": - try { - int def = Integer.parseInt(args.get(1)); - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE)); - CommandHandler.sendMessage(sender, "DEF set to " + def + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid DEF value."); - return; - } - break; - case "atk": - try { - int atk = Integer.parseInt(args.get(1)); - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_ATTACK)); - CommandHandler.sendMessage(sender, "ATK set to " + atk + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid ATK value."); - return; - } - break; - case "em": - try { - int em = Integer.parseInt(args.get(1)); - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - entity.setFightProperty(FightProperty.FIGHT_PROP_ELEMENT_MASTERY, em); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ELEMENT_MASTERY)); - CommandHandler.sendMessage(sender, "Elemental Mastery set to " + em + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid EM value."); - return; - } - break; - case "er": - try { - float er = Integer.parseInt(args.get(1)); - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - float erecharge = er / 10000; - entity.setFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, erecharge); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY)); - float iger = erecharge * 100; - CommandHandler.sendMessage(sender, "Energy recharge set to " + iger + "%."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid ER value."); - return; - } - break; - case "crate": - try { - float cr = Integer.parseInt(args.get(1)); - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - float crate = cr / 10000; - entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL, crate); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL)); - float igcrate = crate * 100; - CommandHandler.sendMessage(sender, "Crit Rate set to " + igcrate + "%."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid Crit Rate value."); - return; - } - break; - case "cdmg": - try { - float cdmg = Integer.parseInt(args.get(1)); - EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); - float cdamage = cdmg / 10000; - entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, cdamage); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL_HURT)); - float igcdmg = cdamage * 100; - CommandHandler.sendMessage(sender, "Crit DMG set to " + igcdmg + "%"); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid Crit DMG value."); - return; - } - break; - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; + +import java.util.List; + +@Command(label = "setstats", usage = "setstats ", + aliases = {"stats"}) +public final class SetStatsCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + String stat = args.get(0); + switch (stat) { + default: + CommandHandler.sendMessage(sender, "Usage: setstats|stats "); + return; + case "hp": + try { + int health = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + CommandHandler.sendMessage(sender, "HP set to " + health + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid HP value."); + return; + } + break; + case "def": + try { + int def = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE)); + CommandHandler.sendMessage(sender, "DEF set to " + def + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid DEF value."); + return; + } + break; + case "atk": + try { + int atk = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_ATTACK)); + CommandHandler.sendMessage(sender, "ATK set to " + atk + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid ATK value."); + return; + } + break; + case "em": + try { + int em = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_ELEMENT_MASTERY, em); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ELEMENT_MASTERY)); + CommandHandler.sendMessage(sender, "Elemental Mastery set to " + em + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid EM value."); + return; + } + break; + case "er": + try { + float er = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float erecharge = er / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, erecharge); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY)); + float iger = erecharge * 100; + CommandHandler.sendMessage(sender, "Energy recharge set to " + iger + "%."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid ER value."); + return; + } + break; + case "crate": + try { + float cr = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float crate = cr / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL, crate); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL)); + float igcrate = crate * 100; + CommandHandler.sendMessage(sender, "Crit Rate set to " + igcrate + "%."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Crit Rate value."); + return; + } + break; + case "cdmg": + try { + float cdmg = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + float cdamage = cdmg / 10000; + entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, cdamage); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL_HURT)); + float igcdmg = cdamage * 100; + CommandHandler.sendMessage(sender, "Crit DMG set to " + igcdmg + "%"); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Crit DMG value."); + return; + } + break; + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java b/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java similarity index 93% rename from src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java rename to src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java index 2619ee3b6..146837523 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java +++ b/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java @@ -1,39 +1,39 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.props.PlayerProperty; - -import java.util.List; - -@Command(label = "setworldlevel", usage = "setworldlevel ", - description = "Sets your world level (Relog to see proper effects)", - aliases = {"setworldlvl"}, permission = "player.setworldlevel") -public class SetWorldLevel implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; // TODO: set player's world level from console or other players - } - - if (args.size() < 1) { - CommandHandler.sendMessage(sender, "Usage: setworldlevel "); - return; - } - - try { - int level = Integer.parseInt(args.get(0)); - - // Set in both world and player props - sender.getWorld().setWorldLevel(level); - sender.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level); - - sender.dropMessage("World level set to " + level + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid world level."); - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.props.PlayerProperty; + +import java.util.List; + +@Command(label = "setworldlevel", usage = "setworldlevel ", + description = "Sets your world level (Relog to see proper effects)", + aliases = {"setworldlvl"}, permission = "player.setworldlevel") +public final class SetWorldLevelCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: set player's world level from console or other players + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: setworldlevel "); + return; + } + + try { + int level = Integer.parseInt(args.get(0)); + + // Set in both world and player props + sender.getWorld().setWorldLevel(level); + sender.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level); + + sender.dropMessage("World level set to " + level + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid world level."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Spawn.java b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java similarity index 95% rename from src/main/java/emu/grasscutter/command/commands/Spawn.java rename to src/main/java/emu/grasscutter/command/commands/SpawnCommand.java index 5a3fc2b9c..969872119 100644 --- a/src/main/java/emu/grasscutter/command/commands/Spawn.java +++ b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java @@ -1,51 +1,51 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.MonsterData; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.entity.EntityMonster; -import emu.grasscutter.utils.Position; - -import java.util.List; - -@Command(label = "spawn", usage = "spawn [level] [amount]", - description = "Spawns an entity near you", permission = "server.spawn") -public class Spawn implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; - } - - if (args.size() < 1) { - CommandHandler.sendMessage(sender, "Usage: spawn [amount]"); - return; - } - - try { - int entity = Integer.parseInt(args.get(0)); - int level = args.size() > 1 ? Integer.parseInt(args.get(1)) : 1; - int amount = args.size() > 2 ? Integer.parseInt(args.get(2)) : 1; - - MonsterData entityData = GenshinData.getMonsterDataMap().get(entity); - if (entityData == null) { - CommandHandler.sendMessage(sender, "Invalid entity id."); - return; - } - - float range = (5f + (.1f * amount)); - for (int i = 0; i < amount; i++) { - Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityMonster monster = new EntityMonster(sender.getScene(), entityData, pos, level); - sender.getScene().addEntity(monster); - } - CommandHandler.sendMessage(sender, String.format("Spawned %s of %s.", amount, entity)); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid item or player ID."); - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.utils.Position; + +import java.util.List; + +@Command(label = "spawn", usage = "spawn [level] [amount]", + description = "Spawns an entity near you", permission = "server.spawn") +public final class SpawnCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: spawn [amount]"); + return; + } + + try { + int entity = Integer.parseInt(args.get(0)); + int level = args.size() > 1 ? Integer.parseInt(args.get(1)) : 1; + int amount = args.size() > 2 ? Integer.parseInt(args.get(2)) : 1; + + MonsterData entityData = GenshinData.getMonsterDataMap().get(entity); + if (entityData == null) { + CommandHandler.sendMessage(sender, "Invalid entity id."); + return; + } + + float range = (5f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityMonster monster = new EntityMonster(sender.getScene(), entityData, pos, level); + sender.getScene().addEntity(monster); + } + CommandHandler.sendMessage(sender, String.format("Spawned %s of %s.", amount, entity)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Stop.java b/src/main/java/emu/grasscutter/command/commands/StopCommand.java similarity index 90% rename from src/main/java/emu/grasscutter/command/commands/Stop.java rename to src/main/java/emu/grasscutter/command/commands/StopCommand.java index a76e33435..4b63cd44e 100644 --- a/src/main/java/emu/grasscutter/command/commands/Stop.java +++ b/src/main/java/emu/grasscutter/command/commands/StopCommand.java @@ -1,23 +1,23 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -@Command(label = "stop", usage = "stop", - description = "Stops the server", permission = "server.stop") -public class Stop implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - CommandHandler.sendMessage(null, "Server shutting down..."); - for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { - CommandHandler.sendMessage(p, "Server shutting down..."); - } - - System.exit(1); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "stop", usage = "stop", + description = "Stops the server", permission = "server.stop") +public final class StopCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + CommandHandler.sendMessage(null, "Server shutting down..."); + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + CommandHandler.sendMessage(p, "Server shutting down..."); + } + + System.exit(1); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Weather.java b/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java similarity index 93% rename from src/main/java/emu/grasscutter/command/commands/Weather.java rename to src/main/java/emu/grasscutter/command/commands/WeatherCommand.java index ff3c77044..919432ed1 100644 --- a/src/main/java/emu/grasscutter/command/commands/Weather.java +++ b/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java @@ -1,39 +1,39 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.props.ClimateType; -import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify; - -import java.util.List; - -@Command(label = "weather", usage = "weather ", - description = "Changes the weather.", aliases = {"w"}, permission = "player.weather") -public class Weather implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; - } - - if (args.size() < 1) { - CommandHandler.sendMessage(sender, "Usage: weather "); - return; - } - - try { - int weatherId = Integer.parseInt(args.get(0)); - - ClimateType climate = ClimateType.getTypeByValue(weatherId); - - sender.getScene().setClimate(climate); - sender.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(sender)); - CommandHandler.sendMessage(sender, "Changed weather to " + weatherId); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid weather ID."); - } - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.props.ClimateType; +import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify; + +import java.util.List; + +@Command(label = "weather", usage = "weather ", + description = "Changes the weather.", aliases = {"w"}, permission = "player.weather") +public final class WeatherCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: weather "); + return; + } + + try { + int weatherId = Integer.parseInt(args.get(0)); + + ClimateType climate = ClimateType.getTypeByValue(weatherId); + + sender.getScene().setClimate(climate); + sender.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(sender)); + CommandHandler.sendMessage(sender, "Changed weather to " + weatherId); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid weather ID."); + } + } +} From e100410d4e844e6991582eda8ba4f9fa4e4c8578 Mon Sep 17 00:00:00 2001 From: JuliusP <93566768+JuliusPtolemy@users.noreply.github.com> Date: Thu, 21 Apr 2022 08:56:15 +0800 Subject: [PATCH 39/91] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aedab22f2..299093c4c 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ There is a dummy user named "Server" in every player's friends list that you can `!resetconst` - Resets the constellation level on your current active character, will need to relog after using the command to see any changes. -`!sethp [hp]` +`!setstats [stat] [value]` `!clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including yellow rarity ones** from your inventory From 708ee0214cb6fca3af4c3eb85dce757d8ec4eeba Mon Sep 17 00:00:00 2001 From: JuliusP <93566768+JuliusPtolemy@users.noreply.github.com> Date: Thu, 21 Apr 2022 08:57:32 +0800 Subject: [PATCH 40/91] Delete add-to-wiki directory --- add-to-wiki/resources-fun.md | 78 ------------------------------------ 1 file changed, 78 deletions(-) delete mode 100644 add-to-wiki/resources-fun.md diff --git a/add-to-wiki/resources-fun.md b/add-to-wiki/resources-fun.md deleted file mode 100644 index 6b1afea25..000000000 --- a/add-to-wiki/resources-fun.md +++ /dev/null @@ -1,78 +0,0 @@ -# Fun -## Prerequisites -- [MongoDBCompass](https://www.mongodb.com/try/download/compass) - -*** - -### Colored nickname and signature :peacock: ([written by](https://github.com/actuallyeunha)) -Unity supports colored text by default (See [Unity Manual](https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/StyledText.html) for more info) - -By replacing them with `text` or `text` you can change most, if not all, strings' colors. - - -1. Open MongoDBCompass and connect to your db -2. Go to `grasscutter/players` -3. Make your changes. *Change it inside `playerProfile` too!*
- e.g `nickname: "na.na"`;
- `signature: "Running on Grasscutter!"` -4. Update the document -5. Relog to see changes - -Enjoy your colorful name :sparkles: - - -*** -## Avatar/Character -This guide is recommended if you have the character/weapon. If you want to get a character at Level Z, then use !givechar [avatarID] [level] -### Changing Level/Ascension/Talents -> Make sure you have created an account with the avatars/characters. -1. Open **MongoDBCompass** -2. Connect to the host (default URI is `mongodb://localhost:27017`) -3. Navigate to Databases > `grasscutter` > `avatars` -4. (Optional) Set **View** to `JSON View` -5. In the **Filter** field, type `{ avatarId: X }` where `X` is the Avatar ID that you are trying to modify. **Avatar IDs are 8-digits.** -6. Click the **Find** next to the field. -7. After the documents have been filtered, edit the document - 1. To change character **level**, change the value next to `"level"` - 2. To change character **ascension**, change the value next to `"promoteLevel"` - Check [Wiki](https://genshin-impact.fandom.com/wiki/Characters#:~:text=one%20Acquaint%20Fate.-,Ascension%20Phase,-Max%20Char.%20Level) for ascension values - 3. To change **talents**, expand `"proudSkillList"` - 1. To unlock the avatar's **1st Ascension Passive**, add XX2101 inside `"proudSkillList"`, where **XX is the last 2 digits of the Avatar ID**. **Make sure to add a `,` for each line.** - 2. To unlock the avatar's **4th Ascension Passive**, do the same as before, but replace `2101` with `2201`. -8. After editing, you can now **Replace**. - -### Example -A document in JSON view that has the Avatar ID 10000058, level 90, ascension phase 6, all talents unlocked. - - - - -### Notes -- On the last line of `"proudSkillList"`, a `,` is no longer needed. -- After replacing the document, make sure to restart the server for changes to take effect. - - -## Weapons -> Make sure you have created an account with the weapons. -1. Open **MongoDBCompass** -2. Connect to the host (default URI is `mongodb://localhost:27017`) -3. Navigate to Databases > `grasscutter` > `items` -4. (Optional) Set **View** to `JSON View` -5. In the **Filter** field, type `{ itemId: Y }` where `Y` is the Item/Weapon ID that you are trying to modify. **Weapon IDs are 5 digits.** -6. Click the **Find** next to the field. -7. After the documents have been filtered, edit the document - 1. To change weapon **level**, change the value next to `"level"` - 2. To change weapon **ascension**, change the value next to `"promoteLevel"` - Check [Wiki](https://genshin-impact.fandom.com/wiki/Weapons#:~:text=reaching%202nd%20Ascension.-,Ascension%20Phase,-Max%20Weapon%20Level) for ascension values - 3. To change **refinement**, change the value next to `"refinement"` - * Refinement Rank 1 = 0 - * Refinement Rank 2 = 1 - * Refinement Rank 3 = 2 - * Refinement Rank 4 = 3 - * Refinement Rank 5 = 4 -8. After editing, you can now **Replace**. - -### Example -A document in JSON view that has the Weapon ID 12503, level 90, ascension phase 6, refinement rank 5. - - From b9876fc0561800d61d1e281b5f55882d5013369f Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 18:37:07 -0700 Subject: [PATCH 41/91] Fix water and the visual map in archipelago --- .../server/packet/send/PacketSceneUnlockInfoNotify.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java index 857f9879c..fb45bb361 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java @@ -13,7 +13,7 @@ public class PacketSceneUnlockInfoNotify extends GenshinPacket { SceneUnlockInfoNotify proto = SceneUnlockInfoNotify.newBuilder() .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(1)) .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(3)) - .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(4)) + .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(4).addSceneTagIdList(106).addSceneTagIdList(109)) .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(5)) .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(6)) .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(7)) From 7f2de4fab85b7081524e3a8f3d71616af46802ac Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 18:44:02 -0700 Subject: [PATCH 42/91] Fix player location updates in co-op --- .../emu/grasscutter/game/GenshinPlayer.java | 26 ++++++++++++++++++- .../packet/recv/HandlerEnterSceneDoneReq.java | 5 +++- .../send/PacketScenePlayerLocationNotify.java | 7 ++--- .../send/PacketWorldPlayerLocationNotify.java | 2 +- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 1d8650e54..b60a5e7ac 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -31,6 +31,7 @@ import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo; import emu.grasscutter.net.proto.PlayerApplyEnterMpReasonOuterClass.PlayerApplyEnterMpReason; import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; +import emu.grasscutter.net.proto.WorldPlayerLocationInfoOuterClass.WorldPlayerLocationInfo; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketAbilityInvocationsNotify; @@ -49,9 +50,11 @@ import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify; import emu.grasscutter.server.packet.send.PacketPlayerPropNotify; import emu.grasscutter.server.packet.send.PacketPlayerStoreNotify; import emu.grasscutter.server.packet.send.PacketPrivateChatNotify; +import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify; import emu.grasscutter.server.packet.send.PacketSetNameCardRsp; import emu.grasscutter.server.packet.send.PacketStoreWeightLimitNotify; import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify; +import emu.grasscutter.server.packet.send.PacketWorldPlayerLocationNotify; import emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify; import emu.grasscutter.utils.Position; @@ -101,6 +104,7 @@ public class GenshinPlayer { @Transient private int enterSceneToken; @Transient private SceneLoadState sceneState; @Transient private boolean hasSentAvatarDataNotify; + @Transient private long nextSendPlayerLocTime = 0; @Transient private final Int2ObjectMap coopRequests; @Transient private final InvokeHandler combatInvokeHandler; @@ -654,6 +658,13 @@ public class GenshinPlayer { return social; } + public WorldPlayerLocationInfo getWorldPlayerLocationInfo() { + return WorldPlayerLocationInfo.newBuilder() + .setSceneId(this.getSceneId()) + .setPlayerLoc(this.getPlayerLocationInfo()) + .build(); + } + public PlayerLocationInfo getPlayerLocationInfo() { return PlayerLocationInfo.newBuilder() .setUid(this.getUid()) @@ -679,9 +690,22 @@ public class GenshinPlayer { } // Ping if (this.getWorld() != null) { - this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld())); // Player ping + // RTT notify - very important to send this often + this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld())); + + // Update player locations if in multiplayer every 5 seconds + long time = System.currentTimeMillis(); + if (this.getWorld().isMultiplayer() && this.getScene() != null && time > nextSendPlayerLocTime) { + this.sendPacket(new PacketWorldPlayerLocationNotify(this.getWorld())); + this.sendPacket(new PacketScenePlayerLocationNotify(this.getScene())); + this.resetSendPlayerLocTime(); + } } } + + public void resetSendPlayerLocTime() { + this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000; + } @PostLoad private void onLoad() { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterSceneDoneReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterSceneDoneReq.java index 5b56a39f4..2f7da6884 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterSceneDoneReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterSceneDoneReq.java @@ -31,8 +31,11 @@ public class HandlerEnterSceneDoneReq extends PacketHandler { // Locations session.send(new PacketWorldPlayerLocationNotify(session.getPlayer().getWorld())); - session.send(new PacketScenePlayerLocationNotify(session.getPlayer())); + session.send(new PacketScenePlayerLocationNotify(session.getPlayer().getScene())); session.send(new PacketWorldPlayerRTTNotify(session.getPlayer().getWorld())); + + // Reset timer for sending player locations + session.getPlayer().resetSendPlayerLocTime(); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerLocationNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerLocationNotify.java index f6fa9b8f0..2e9fb479d 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerLocationNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerLocationNotify.java @@ -1,19 +1,20 @@ package emu.grasscutter.server.packet.send; import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.GenshinScene; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.ScenePlayerLocationNotifyOuterClass.ScenePlayerLocationNotify; public class PacketScenePlayerLocationNotify extends GenshinPacket { - public PacketScenePlayerLocationNotify(GenshinPlayer player) { + public PacketScenePlayerLocationNotify(GenshinScene scene) { super(PacketOpcodes.ScenePlayerLocationNotify); ScenePlayerLocationNotify.Builder proto = ScenePlayerLocationNotify.newBuilder() - .setSceneId(player.getSceneId()); + .setSceneId(scene.getId()); - for (GenshinPlayer p : player.getWorld().getPlayers()) { + for (GenshinPlayer p : scene.getPlayers()) { proto.addPlayerLocList(p.getPlayerLocationInfo()); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerLocationNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerLocationNotify.java index cb546f0e7..1bb09f8db 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerLocationNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWorldPlayerLocationNotify.java @@ -14,7 +14,7 @@ public class PacketWorldPlayerLocationNotify extends GenshinPacket { WorldPlayerLocationNotify.Builder proto = WorldPlayerLocationNotify.newBuilder(); for (GenshinPlayer p : world.getPlayers()) { - proto.addPlayerLocList(p.getPlayerLocationInfo()); + proto.addPlayerLocList(p.getWorldPlayerLocationInfo()); } this.setData(proto); From 8a8906098ef984265c9145971a45ecba0fe04bce Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 18:48:26 -0700 Subject: [PATCH 43/91] Fix not being able to join friend's world --- .../emu/grasscutter/game/GenshinPlayer.java | 20 +++++++++---------- .../game/friends/PlayerProfile.java | 1 + .../send/PacketGetPlayerFriendListRsp.java | 1 - 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index b60a5e7ac..c7ac82bc6 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -125,6 +125,12 @@ public class GenshinPlayer { } this.properties.put(prop.getId(), 0); } + + this.gachaInfo = new PlayerGachaInfo(); + this.nameCardList = new HashSet<>(); + this.flyCloakList = new HashSet<>(); + this.costumeList = new HashSet<>(); + this.setSceneId(3); this.setRegionId(1); this.sceneState = SceneLoadState.NONE; @@ -144,11 +150,6 @@ public class GenshinPlayer { this.nickname = "Traveler"; this.signature = ""; this.teamManager = new TeamManager(this); - this.gachaInfo = new PlayerGachaInfo(); - this.playerProfile = new PlayerProfile(this); - this.nameCardList = new HashSet<>(); - this.flyCloakList = new HashSet<>(); - this.costumeList = new HashSet<>(); this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1); this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1); this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50); @@ -169,6 +170,7 @@ public class GenshinPlayer { public void setUid(int id) { this.id = id; + this.getProfile().syncWithCharacter(this); } public long getNextGenshinGuid() { @@ -720,12 +722,8 @@ public class GenshinPlayer { // Make sure these exist if (this.getTeamManager() == null) { this.teamManager = new TeamManager(this); - } if (this.getGachaInfo() == null) { - this.gachaInfo = new PlayerGachaInfo(); - } if (this.nameCardList == null) { - this.nameCardList = new HashSet<>(); - } if (this.costumeList == null) { - this.costumeList = new HashSet<>(); + } if (this.getProfile().getId() == 0) { + this.getProfile().syncWithCharacter(this); } // Check if player object exists in server diff --git a/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java b/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java index cc7408aa6..067cca28d 100644 --- a/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java +++ b/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java @@ -87,6 +87,7 @@ public class PlayerProfile { return; } + this.id = player.getUid(); this.name = player.getNickname(); this.avatarId = player.getHeadImage(); this.signature = player.getSignature(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java index 9df89af5f..4d22f9603 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java @@ -23,7 +23,6 @@ public class PacketGetPlayerFriendListRsp extends GenshinPacket { .setWorldLevel(0) .setSignature("") .setLastActiveTime((int) (System.currentTimeMillis() / 1000f)) - .setIsMpModeAvailable(true) .setNameCardId(210001) .setOnlineState(FriendOnlineState.FRIEND_ONLINE) .setParam(1) From f6df468e47dc6cb47f69fd5120295c882f8cef86 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Thu, 21 Apr 2022 10:37:24 +0800 Subject: [PATCH 44/91] folder check --- src/main/java/emu/grasscutter/data/ResourceLoader.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index e597257ce..8a29f5a72 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -129,6 +129,12 @@ public class ResourceLoader { private static void loadScenePoints() { Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)"); File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutPut/Scene/Point"); + + if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) { + Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!"); + return; + } + List scenePointList = new ArrayList<>(); for (File file : folder.listFiles()) { ScenePointConfig config = null; From 4ab12f418ed7fc10cd1343f7a0484349b3f9ee8f Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Thu, 21 Apr 2022 11:43:31 +0800 Subject: [PATCH 45/91] add description and permission to setstats command --- .../java/emu/grasscutter/command/commands/SetStatsCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java index afc1a3e14..e9c4254b6 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java @@ -10,7 +10,7 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import java.util.List; @Command(label = "setstats", usage = "setstats|stats ", - aliases = {"stats"}) + description = "Set fight property for the specific player", aliases = {"stats"}, permission = "player.setstats") public final class SetStatsCommand implements CommandHandler { @Override From f3ede17d4d709907e39b3296502f3b227fafd9fa Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Thu, 21 Apr 2022 11:52:53 +0800 Subject: [PATCH 46/91] change description --- .../java/emu/grasscutter/command/commands/SetStatsCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java index e9c4254b6..78f15de2d 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java @@ -10,7 +10,7 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import java.util.List; @Command(label = "setstats", usage = "setstats|stats ", - description = "Set fight property for the specific player", aliases = {"stats"}, permission = "player.setstats") + description = "Set fight property for your current active character", aliases = {"stats"}, permission = "player.setstats") public final class SetStatsCommand implements CommandHandler { @Override From 1250194480598677574e43209af0c001cdede797 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 21:03:18 -0700 Subject: [PATCH 47/91] Fix weapon skills (and artifact sets) not working when you equip them --- .../game/avatar/GenshinAvatar.java | 47 ++++++++++++------- .../grasscutter/game/entity/EntityAvatar.java | 4 +- .../game/managers/InventoryManager.java | 28 +++-------- .../send/PacketAbilityChangeNotify.java | 4 +- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index a5dcb3ee8..9723c23d7 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -13,6 +13,7 @@ import dev.morphia.annotations.Indexed; import dev.morphia.annotations.PostLoad; import dev.morphia.annotations.PrePersist; import dev.morphia.annotations.Transient; +import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.custom.OpenConfigEntry; @@ -42,6 +43,7 @@ import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo; import emu.grasscutter.net.proto.AvatarInfoOuterClass.AvatarInfo; +import emu.grasscutter.server.packet.send.PacketAbilityChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarFightPropNotify; import emu.grasscutter.utils.ProtoHelper; @@ -69,7 +71,7 @@ public class GenshinAvatar { @Transient private final Int2ObjectMap equips; @Transient private final Int2FloatOpenHashMap fightProp; - @Transient private final Set bonusAbilityList; + @Transient private Set extraAbilityEmbryos; private Map skillLevelMap; // Talent levels private Map proudSkillBonusMap; // Talent bonus levels (from const) @@ -86,7 +88,7 @@ public class GenshinAvatar { // Morhpia only! this.equips = new Int2ObjectOpenHashMap<>(); this.fightProp = new Int2FloatOpenHashMap(); - this.bonusAbilityList = new HashSet<>(); + this.extraAbilityEmbryos = new HashSet<>(); this.proudSkillBonusMap = new HashMap<>(); // TODO Move to genshin avatar } @@ -260,8 +262,8 @@ public class GenshinAvatar { return proudSkillBonusMap; } - public Set getBonusAbilityList() { - return bonusAbilityList; + public Set getExtraAbilityEmbryos() { + return extraAbilityEmbryos; } public float getCurrentHp() { @@ -347,14 +349,14 @@ public class GenshinAvatar { item.setEquipCharacter(this.getAvatarId()); item.save(); + if (this.getPlayer().hasSentAvatarDataNotify()) { + this.getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(this, item)); + } + if (shouldRecalc) { this.recalcStats(); } - if (this.getPlayer().hasSentAvatarDataNotify()) { - this.getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(this, item)); - } - return true; } @@ -371,11 +373,18 @@ public class GenshinAvatar { } public void recalcStats() { + recalcStats(false); + } + + public void recalcStats(boolean forceSendAbilityChange) { // Setup AvatarData data = this.getAvatarData(); AvatarPromoteData promoteData = GenshinData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel()); Int2IntOpenHashMap setMap = new Int2IntOpenHashMap(); - this.getBonusAbilityList().clear(); + + // Extra ability embryos + Set prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos(); + this.extraAbilityEmbryos = new HashSet<>(); // Get hp percent, set to 100% if none float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); @@ -458,7 +467,7 @@ public class GenshinAvatar { } // Add any skill strings from this affix - this.addToAbilityList(affix.getOpenConfig(), true); + this.addToExtraAbilityEmbryos(affix.getOpenConfig(), true); } else { break; } @@ -505,7 +514,7 @@ public class GenshinAvatar { } // Add any skill strings from this affix - this.addToAbilityList(affix.getOpenConfig(), true); + this.addToExtraAbilityEmbryos(affix.getOpenConfig(), true); } } } @@ -538,7 +547,7 @@ public class GenshinAvatar { } // Add any skill strings from this proud skill - this.addToAbilityList(proudSkillData.getOpenConfig(), true); + this.addToExtraAbilityEmbryos(proudSkillData.getOpenConfig(), true); } // Constellations @@ -550,7 +559,7 @@ public class GenshinAvatar { } // Add any skill strings from this constellation - this.addToAbilityList(avatarTalentData.getOpenConfig(), false); + this.addToExtraAbilityEmbryos(avatarTalentData.getOpenConfig(), false); } } @@ -573,11 +582,17 @@ public class GenshinAvatar { // Packet if (getPlayer() != null && getPlayer().hasSentAvatarDataNotify()) { + // Update stats for client getPlayer().sendPacket(new PacketAvatarFightPropNotify(this)); + // Update client abilities + EntityAvatar entity = this.getAsEntity(); + if (entity != null && (!this.getExtraAbilityEmbryos().equals(prevExtraAbilityEmbryos) || forceSendAbilityChange)) { + getPlayer().sendPacket(new PacketAbilityChangeNotify(entity)); + } } } - public void addToAbilityList(String openConfig, boolean forceAdd) { + public void addToExtraAbilityEmbryos(String openConfig, boolean forceAdd) { if (openConfig == null || openConfig.length() == 0) { return; } @@ -586,14 +601,14 @@ public class GenshinAvatar { if (entry == null) { if (forceAdd) { // Add config string to ability skill list anyways - this.getBonusAbilityList().add(openConfig); + this.getExtraAbilityEmbryos().add(openConfig); } return; } if (entry.getAddAbilities() != null) { for (String ability : entry.getAddAbilities()) { - this.getBonusAbilityList().add(ability); + this.getExtraAbilityEmbryos().add(ability); } } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index 86e8ea458..233da5288 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -223,8 +223,8 @@ public class EntityAvatar extends GenshinEntity { } } // Add equip abilities - if (this.getAvatar().getBonusAbilityList().size() > 0) { - for (String skill : this.getAvatar().getBonusAbilityList()) { + if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) { + for (String skill : this.getAvatar().getExtraAbilityEmbryos()) { AbilityEmbryo emb = AbilityEmbryo.newBuilder() .setAbilityId(++embryoId) .setAbilityNameHash(Utils.abilityHash(skill)) diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index 935652d77..532155db1 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -589,7 +589,6 @@ public class InventoryManager { // Update proud skills AvatarSkillDepotData skillDepot = GenshinData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId()); - boolean hasAddedProudSkill = false; if (skillDepot != null && skillDepot.getInherentProudSkillOpens() != null) { for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { @@ -599,7 +598,6 @@ public class InventoryManager { if (openData.getNeedAvatarPromoteLevel() == avatar.getPromoteLevel()) { int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; if (GenshinData.getProudSkillDataMap().containsKey(proudSkillId)) { - hasAddedProudSkill = true; avatar.getProudSkillList().add(proudSkillId); player.sendPacket(new PacketProudSkillChangeNotify(avatar)); } @@ -607,20 +605,13 @@ public class InventoryManager { } } - // Racalc stats and save avatar - avatar.recalcStats(); - avatar.save(); - - // Resend ability embryos if proud skill has been added - if (hasAddedProudSkill && avatar.getAsEntity() != null) { - player.sendPacket(new PacketAbilityChangeNotify(avatar.getAsEntity())); - } - - // TODO Send entity prop update packet to world - // Packets player.sendPacket(new PacketAvatarPropNotify(avatar)); player.sendPacket(new PacketAvatarPromoteRsp(avatar)); + + // TODO Send entity prop update packet to world + avatar.recalcStats(true); + avatar.save(); } public void upgradeAvatar(GenshinPlayer player, long guid, int itemId, int count) { @@ -827,25 +818,20 @@ public class InventoryManager { // Apply + recalc avatar.getTalentIdList().add(talentData.getId()); avatar.setCoreProudSkillLevel(currentTalentLevel + 1); - avatar.recalcStats(); // Packet player.sendPacket(new PacketAvatarUnlockTalentNotify(avatar, nextTalentId)); player.sendPacket(new PacketUnlockAvatarTalentRsp(avatar, nextTalentId)); - // Proud skill bonus map + // Proud skill bonus map (Extra skills) OpenConfigEntry entry = GenshinData.getOpenConfigEntries().get(talentData.getOpenConfig()); if (entry != null && entry.getExtraTalentIndex() > 0) { avatar.recalcProudSkillBonusMap(); player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex())); } - // Resend ability embryos - if (avatar.getAsEntity() != null) { - player.sendPacket(new PacketAbilityChangeNotify(avatar.getAsEntity())); - } - - // Save avatar + // Recalc + save avatar + avatar.recalcStats(true); avatar.save(); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAbilityChangeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAbilityChangeNotify.java index a6e1fb452..d8cdfdef8 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketAbilityChangeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAbilityChangeNotify.java @@ -8,8 +8,8 @@ import emu.grasscutter.net.proto.AbilityChangeNotifyOuterClass.AbilityChangeNoti public class PacketAbilityChangeNotify extends GenshinPacket { public PacketAbilityChangeNotify(EntityAvatar entity) { - super(PacketOpcodes.AbilityChangeNotify); - + super(PacketOpcodes.AbilityChangeNotify, true); + AbilityChangeNotify proto = AbilityChangeNotify.newBuilder() .setEntityId(entity.getId()) .setAbilityControlBlock(entity.getAbilityControlBlock()) From b99ca4e02f65ad019214dc8bf8fdcfdb75a2b869 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 21:33:58 -0700 Subject: [PATCH 48/91] Refactor PacketSceneTransToPointRsp and add a null check when getting ScenePointEntries --- .../command/commands/ChangeSceneCommand.java | 10 +++++-- src/main/java/emu/grasscutter/game/World.java | 21 +------------- .../recv/HandlerSceneTransToPointReq.java | 19 +++++++++++- .../send/PacketSceneTransToPointRsp.java | 29 +++++++++---------- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java b/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java index a5afe7bf7..eb2e998f2 100644 --- a/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java @@ -23,11 +23,17 @@ public final class ChangeSceneCommand implements CommandHandler { try { int sceneId = Integer.parseInt(args.get(0)); + + if (sceneId == sender.getSceneId()) { + CommandHandler.sendMessage(sender, "You are already in that scene"); + return; + } + boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, sender.getPos()); - CommandHandler.sendMessage(sender, "Changed to scene " + sceneId); + if (!result) { - CommandHandler.sendMessage(sender, "Scene does not exist or you are already in it"); + CommandHandler.sendMessage(sender, "Scene does not exist"); } } catch (Exception e) { CommandHandler.sendMessage(sender, "Usage: changescene "); diff --git a/src/main/java/emu/grasscutter/game/World.java b/src/main/java/emu/grasscutter/game/World.java index 4375d8e4f..f88d3bee4 100644 --- a/src/main/java/emu/grasscutter/game/World.java +++ b/src/main/java/emu/grasscutter/game/World.java @@ -206,28 +206,9 @@ public class World implements Iterable { public void deregisterScene(GenshinScene scene) { this.getScenes().remove(scene.getId()); } - - public boolean forceTransferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { - // Forces the client to reload the scene map to prevent the player from falling off the map. - if (GenshinData.getSceneDataMap().get(sceneId) == null) { - return false; - } - - if (player.getScene() != null) { - player.getScene().removePlayer(player); - } - - GenshinScene scene = this.getSceneById(sceneId); - scene.addPlayer(player); - player.getPos().set(pos); - - // Teleport packet - player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterSelf, EnterReason.TransPoint, sceneId, pos)); - return true; - } public boolean transferPlayerToScene(GenshinPlayer player, int sceneId, Position pos) { - if (player.getScene().getId() == sceneId || GenshinData.getSceneDataMap().get(sceneId) == null) { + if (GenshinData.getSceneDataMap().get(sceneId) == null) { return false; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java index 619a37b1e..f01f5980d 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java @@ -1,11 +1,15 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.custom.ScenePointEntry; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.SceneTransToPointReqOuterClass.SceneTransToPointReq; +import emu.grasscutter.net.proto.SceneTransToPointRspOuterClass.SceneTransToPointRsp; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketSceneTransToPointRsp; +import emu.grasscutter.utils.Position; @Opcodes(PacketOpcodes.SceneTransToPointReq) public class HandlerSceneTransToPointReq extends PacketHandler { @@ -13,7 +17,20 @@ public class HandlerSceneTransToPointReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { SceneTransToPointReq req = SceneTransToPointReq.parseFrom(payload); - session.send(new PacketSceneTransToPointRsp(session.getPlayer(), req.getPointId(), req.getSceneId())); + + String code = req.getSceneId() + "_" + req.getPointId(); + ScenePointEntry scenePointEntry = GenshinData.getScenePointEntries().get(code); + + if (scenePointEntry != null) { + float x = scenePointEntry.getPointData().getTranPos().getX(); + float y = scenePointEntry.getPointData().getTranPos().getY(); + float z = scenePointEntry.getPointData().getTranPos().getZ(); + + session.getPlayer().getWorld().transferPlayerToScene(session.getPlayer(), req.getSceneId(), new Position(x, y, z)); + session.send(new PacketSceneTransToPointRsp(session.getPlayer(), req.getPointId(), req.getSceneId())); + } else { + session.send(new PacketSceneTransToPointRsp()); + } } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java index 965c6aa6b..4795c5e9f 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java @@ -13,23 +13,22 @@ public class PacketSceneTransToPointRsp extends GenshinPacket { public PacketSceneTransToPointRsp(GenshinPlayer player, int pointId, int sceneId) { super(PacketOpcodes.SceneTransToPointRsp); - String code = sceneId + "_" + pointId; - ScenePointEntry scenePointEntry = GenshinData.getScenePointEntries().get(code); - - float x = scenePointEntry.getPointData().getTranPos().getX(); - float y = scenePointEntry.getPointData().getTranPos().getY(); - float z = scenePointEntry.getPointData().getTranPos().getZ(); - - player.getPos().set(new Position(x, y, z)); - - player.getWorld().forceTransferPlayerToScene(player, sceneId, player.getPos()); - SceneTransToPointRsp proto = SceneTransToPointRsp.newBuilder() - .setRetcode(0) - .setPointId(pointId) - .setSceneId(sceneId) - .build(); + .setRetcode(0) + .setPointId(pointId) + .setSceneId(sceneId) + .build(); + + this.setData(proto); + } + + public PacketSceneTransToPointRsp() { + super(PacketOpcodes.SceneTransToPointRsp); + SceneTransToPointRsp proto = SceneTransToPointRsp.newBuilder() + .setRetcode(1) // Internal server error + .build(); + this.setData(proto); } } From 2b0831b7def5624d5ab7285946b1fd4678f8760c Mon Sep 17 00:00:00 2001 From: labalityowo <56186498+labalityowo@users.noreply.github.com> Date: Thu, 21 Apr 2022 11:42:21 +0700 Subject: [PATCH 49/91] Remove the stella appearance --- src/main/java/emu/grasscutter/game/gacha/GachaManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index eb1cba379..a38b91812 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -218,7 +218,6 @@ public class GachaManager { addStarglitter = 2; // Add 1 const gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null)); - gachaItem.addTokenItemList(ItemParam.newBuilder().setItemId(constItemId).setCount(1)); player.getInventory().addItem(constItemId, 1); } else { // Is max const From 7fcaa97a6a9dc9284d420eb2befbccfd50eeb04c Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Thu, 21 Apr 2022 12:49:23 +0800 Subject: [PATCH 50/91] add args check --- .../emu/grasscutter/command/commands/SetStatsCommand.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java index 78f15de2d..e3efdb0d5 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java @@ -20,6 +20,11 @@ public final class SetStatsCommand implements CommandHandler { return; } + if (args.size() < 2){ + CommandHandler.sendMessage(sender, "Usage: setstats|stats "); + return; + } + String stat = args.get(0); switch (stat) { default: From af1ecca96313ebcf2977f6047fe464ae46396b04 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Wed, 20 Apr 2022 21:50:33 -0700 Subject: [PATCH 51/91] More refactoring (player id -> player uid) --- .../grasscutter/command/commands/AccountCommand.java | 2 +- .../emu/grasscutter/command/commands/KickCommand.java | 2 +- src/main/java/emu/grasscutter/game/Account.java | 5 +++-- src/main/java/emu/grasscutter/game/GenshinPlayer.java | 2 +- .../java/emu/grasscutter/game/friends/FriendsList.java | 2 +- .../java/emu/grasscutter/game/friends/Friendship.java | 2 +- .../emu/grasscutter/game/friends/PlayerProfile.java | 10 +++++----- .../server/packet/recv/HandlerGetPlayerTokenReq.java | 8 ++++---- .../server/packet/recv/HandlerPlayerLoginReq.java | 2 +- .../packet/recv/HandlerSetPlayerBornDataReq.java | 2 +- .../server/packet/send/PacketGetPlayerTokenRsp.java | 2 +- 11 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java index 2c21cba79..26570e0e8 100644 --- a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java @@ -46,7 +46,7 @@ public final class AccountCommand implements CommandHandler { CommandHandler.sendMessage(null, "Account already exists."); return; } else { - CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); + CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerUid() + "."); account.addPermission("*"); // Grant the player superuser permissions. account.save(); // Save account to database. } diff --git a/src/main/java/emu/grasscutter/command/commands/KickCommand.java b/src/main/java/emu/grasscutter/command/commands/KickCommand.java index 188ce1877..61e9d759b 100644 --- a/src/main/java/emu/grasscutter/command/commands/KickCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/KickCommand.java @@ -22,7 +22,7 @@ public final class KickCommand implements CommandHandler { } if (sender != null) { - CommandHandler.sendMessage(sender, String.format("Player [%s:%s] has kicked player [%s:%s]", sender.getAccount().getPlayerId(), sender.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); + CommandHandler.sendMessage(sender, String.format("Player [%s:%s] has kicked player [%s:%s]", sender.getAccount().getPlayerUid(), sender.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); } CommandHandler.sendMessage(sender, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername())); diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index 2eeeed3b1..dfb1e2829 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -1,5 +1,6 @@ package emu.grasscutter.game; +import dev.morphia.annotations.AlsoLoad; import dev.morphia.annotations.Collation; import dev.morphia.annotations.Entity; import dev.morphia.annotations.Id; @@ -24,7 +25,7 @@ public class Account { private String username; private String password; // Unused for now - private int playerId; + @AlsoLoad("playerUid") private int playerId; private String email; private String token; @@ -68,7 +69,7 @@ public class Account { this.token = token; } - public int getPlayerId() { + public int getPlayerUid() { return this.playerId; } diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index c7ac82bc6..1bc989183 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -722,7 +722,7 @@ public class GenshinPlayer { // Make sure these exist if (this.getTeamManager() == null) { this.teamManager = new TeamManager(this); - } if (this.getProfile().getId() == 0) { + } if (this.getProfile().getUid() == 0) { this.getProfile().syncWithCharacter(this); } diff --git a/src/main/java/emu/grasscutter/game/friends/FriendsList.java b/src/main/java/emu/grasscutter/game/friends/FriendsList.java index 582a10efd..45a11fb1c 100644 --- a/src/main/java/emu/grasscutter/game/friends/FriendsList.java +++ b/src/main/java/emu/grasscutter/game/friends/FriendsList.java @@ -220,7 +220,7 @@ public class FriendsList { friendship.setOwner(getPlayer()); // Check if friend is online - GenshinPlayer friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getId()); + GenshinPlayer friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getUid()); if (friend != null) { // Set friend to online mode friendship.setFriendProfile(friend); diff --git a/src/main/java/emu/grasscutter/game/friends/Friendship.java b/src/main/java/emu/grasscutter/game/friends/Friendship.java index 2be7d1fce..172d1bd43 100644 --- a/src/main/java/emu/grasscutter/game/friends/Friendship.java +++ b/src/main/java/emu/grasscutter/game/friends/Friendship.java @@ -88,7 +88,7 @@ public class Friendship { public FriendBrief toProto() { FriendBrief proto = FriendBrief.newBuilder() - .setUid(getFriendProfile().getId()) + .setUid(getFriendProfile().getUid()) .setNickname(getFriendProfile().getName()) .setLevel(getFriendProfile().getPlayerLevel()) .setAvatar(HeadImage.newBuilder().setAvatarId(getFriendProfile().getAvatarId())) diff --git a/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java b/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java index 067cca28d..b9381c901 100644 --- a/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java +++ b/src/main/java/emu/grasscutter/game/friends/PlayerProfile.java @@ -7,7 +7,7 @@ import emu.grasscutter.utils.Utils; public class PlayerProfile { @Transient private GenshinPlayer player; - private int id; + @AlsoLoad("id") private int uid; private int nameCard; private int avatarId; private String name; @@ -22,12 +22,12 @@ public class PlayerProfile { public PlayerProfile() { } public PlayerProfile(GenshinPlayer player) { - this.id = player.getUid(); + this.uid = player.getUid(); this.syncWithCharacter(player); } - public int getId() { - return id; + public int getUid() { + return uid; } public GenshinPlayer getPlayer() { @@ -87,7 +87,7 @@ public class PlayerProfile { return; } - this.id = player.getUid(); + this.uid = player.getUid(); this.name = player.getNickname(); this.avatarId = player.getHeadImage(); this.signature = player.getSignature(); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java index dc686f5c1..5034fb01a 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java @@ -35,15 +35,15 @@ public class HandlerGetPlayerTokenReq extends PacketHandler { // Has character boolean doesPlayerExist = false; - if (account.getPlayerId() > 0) { + if (account.getPlayerUid() > 0) { // Set flag for player existing - doesPlayerExist = DatabaseHelper.checkPlayerExists(account.getPlayerId()); + doesPlayerExist = DatabaseHelper.checkPlayerExists(account.getPlayerUid()); } // Set reserve player id if account doesnt exist if (!doesPlayerExist) { - int id = DatabaseHelper.getNextPlayerId(session.getAccount().getPlayerId()); - if (id != session.getAccount().getPlayerId()) { + int id = DatabaseHelper.getNextPlayerId(session.getAccount().getPlayerUid()); + if (id != session.getAccount().getPlayerUid()) { session.getAccount().setPlayerId(id); session.getAccount().save(); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java index 183ef7461..05753aec0 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java @@ -30,7 +30,7 @@ public class HandlerPlayerLoginReq extends PacketHandler { } // Load character from db - GenshinPlayer player = DatabaseHelper.getPlayerById(session.getAccount().getPlayerId()); + GenshinPlayer player = DatabaseHelper.getPlayerById(session.getAccount().getPlayerUid()); if (player == null) { // Send packets diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java index 5cbbd8d04..72fc709d9 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java @@ -43,7 +43,7 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler { try { // Save to db - DatabaseHelper.createPlayer(player, session.getAccount().getPlayerId()); + DatabaseHelper.createPlayer(player, session.getAccount().getPlayerUid()); // Create avatar if (player.getAvatars().getAvatarCount() == 0) { diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerTokenRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerTokenRsp.java index 2306aa0ae..00f512ea7 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerTokenRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerTokenRsp.java @@ -16,7 +16,7 @@ public class PacketGetPlayerTokenRsp extends GenshinPacket { this.setUseDispatchKey(true); GetPlayerTokenRsp p = GetPlayerTokenRsp.newBuilder() - .setPlayerUid(session.getAccount().getPlayerId()) + .setPlayerUid(session.getAccount().getPlayerUid()) .setAccountToken(session.getAccount().getToken()) .setAccountType(1) .setIsProficientPlayer(doesPlayerExist) // Not sure where this goes From 83fe5818dcddeaa2fe45d56d7f13f029a15222a4 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 13:53:50 +0800 Subject: [PATCH 52/91] Restructured the config file in preperation for server run modes --- src/main/java/emu/grasscutter/Config.java | 70 ++++++++++--------- .../java/emu/grasscutter/Grasscutter.java | 2 +- .../emu/grasscutter/game/GenshinPlayer.java | 2 +- .../java/emu/grasscutter/game/TeamInfo.java | 6 +- .../emu/grasscutter/game/TeamManager.java | 4 +- .../grasscutter/game/gacha/GachaBanner.java | 2 +- .../grasscutter/game/gacha/GachaManager.java | 2 +- .../grasscutter/game/inventory/Inventory.java | 8 +-- .../server/dispatch/DispatchServer.java | 28 ++++---- .../server/game/GameServerPacketHandler.java | 2 +- .../grasscutter/server/game/GameSession.java | 4 +- .../packet/send/PacketPlayerStoreNotify.java | 2 +- .../packet/send/PacketPullRecentChatRsp.java | 6 +- .../send/PacketStoreWeightLimitNotify.java | 10 +-- 14 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index dddbbfa9e..c6748fb41 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -1,20 +1,7 @@ package emu.grasscutter; public final class Config { - public String DispatchServerIp = "127.0.0.1"; - public String DispatchServerPublicIp = ""; - public int DispatchServerPort = 443; - public String DispatchServerKeystorePath = "./keystore.p12"; - public String DispatchServerKeystorePassword = ""; - public Boolean UseSSL = true; - - public String GameServerName = "Test"; - public String GameServerIp = "127.0.0.1"; - public String GameServerPublicIp = ""; - public int GameServerPort = 22102; - - public int UploadLogPort = 80; - + public String DatabaseUrl = "mongodb://localhost:27017"; public String DatabaseCollection = "grasscutter"; @@ -23,26 +10,36 @@ public final class Config { public String PACKETS_FOLDER = "./packets/"; public String DUMPS_FOLDER = "./dumps/"; public String KEY_FOLDER = "./keys/"; - public boolean LOG_PACKETS = false; - public GameRates Game = new GameRates(); - public ServerOptions ServerOptions = new ServerOptions(); - - public GameRates getGameRates() { - return Game; + public GameServerOptions GameServer = new GameServerOptions(); + public DispatchServerOptions DispatchServer = new DispatchServerOptions(); + + public GameServerOptions getGameServerOptions() { + return GameServer; + } + + public DispatchServerOptions getDispatchOptions() { return DispatchServer; } + + public static class DispatchServerOptions { + public String Ip = "127.0.0.1"; + public String PublicIp = ""; + public int Port = 443; + public int UploadLogPort = 80; + public String KeystorePath = "./keystore.p12"; + public String KeystorePassword = ""; + public Boolean UseSSL = true; + + public boolean AutomaticallyCreateAccounts = false; } - public ServerOptions getServerOptions() { - return ServerOptions; - } - - public static class GameRates { - public float ADVENTURE_EXP_RATE = 1.0f; - public float MORA_RATE = 1.0f; - public float DOMAIN_DROP_RATE = 1.0f; - } - - public static class ServerOptions { + public static class GameServerOptions { + public String Name = "Test"; + public String Ip = "127.0.0.1"; + public String PublicIp = ""; + public int Port = 22102; + + public boolean LOG_PACKETS = false; + public int InventoryLimitWeapon = 2000; public int InventoryLimitRelic = 2000; public int InventoryLimitMaterial = 2000; @@ -54,6 +51,15 @@ public final class Config { public boolean WatchGacha = false; public int[] WelcomeEmotes = {2007, 1002, 4010}; public String WelcomeMotd = "Welcome to Grasscutter emu"; - public boolean AutomaticallyCreateAccounts = false; + + public GameRates Game = new GameRates(); + + public GameRates getGameRates() { return Game; } + + public static class GameRates { + public float ADVENTURE_EXP_RATE = 1.0f; + public float MORA_RATE = 1.0f; + public float DOMAIN_DROP_RATE = 1.0f; + } } } diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 0e34c120d..0b8f1efd8 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -76,7 +76,7 @@ public final class Grasscutter { dispatchServer = new DispatchServer(); dispatchServer.start(); - gameServer = new GameServer(new InetSocketAddress(getConfig().GameServerIp, getConfig().GameServerPort)); + gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port)); gameServer.start(); // Open console. diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 1d8650e54..bf841090e 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -288,7 +288,7 @@ public class GenshinPlayer { } private float getExpModifier() { - return Grasscutter.getConfig().getGameRates().ADVENTURE_EXP_RATE; + return Grasscutter.getConfig().getGameServerOptions().getGameRates().ADVENTURE_EXP_RATE; } // Affected by exp rate diff --git a/src/main/java/emu/grasscutter/game/TeamInfo.java b/src/main/java/emu/grasscutter/game/TeamInfo.java index 0a12b1d2c..c60ce8cc0 100644 --- a/src/main/java/emu/grasscutter/game/TeamInfo.java +++ b/src/main/java/emu/grasscutter/game/TeamInfo.java @@ -13,7 +13,7 @@ public class TeamInfo { public TeamInfo() { this.name = ""; - this.avatars = new ArrayList<>(Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam); + this.avatars = new ArrayList<>(Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam); } public String getName() { @@ -37,7 +37,7 @@ public class TeamInfo { } public boolean addAvatar(GenshinAvatar avatar) { - if (size() >= Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam || contains(avatar)) { + if (size() >= Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam || contains(avatar)) { return false; } @@ -57,7 +57,7 @@ public class TeamInfo { } public void copyFrom(TeamInfo team) { - copyFrom(team, Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam); + copyFrom(team, Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam); } public void copyFrom(TeamInfo team, int maxTeamSize) { diff --git a/src/main/java/emu/grasscutter/game/TeamManager.java b/src/main/java/emu/grasscutter/game/TeamManager.java index 4cd7bfcdf..065c3bd28 100644 --- a/src/main/java/emu/grasscutter/game/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/TeamManager.java @@ -164,13 +164,13 @@ public class TeamManager { public int getMaxTeamSize() { if (getPlayer().isInMultiplayer()) { - int max = Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeamMultiplayer; + int max = Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeamMultiplayer; if (getPlayer().getWorld().getHost() == this.getPlayer()) { return Math.max(1, (int) Math.ceil(max / (double) getWorld().getPlayerCount())); } return Math.max(1, (int) Math.floor(max / (double) getWorld().getPlayerCount())); } - return Grasscutter.getConfig().getServerOptions().MaxAvatarsInTeam; + return Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam; } // Methods diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java index 3e6bcdf7e..ed5492b62 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java @@ -92,7 +92,7 @@ public class GachaBanner { } public GachaInfo toProto() { - String record = "http://" + (Grasscutter.getConfig().DispatchServerPublicIp.isEmpty() ? Grasscutter.getConfig().DispatchServerIp : Grasscutter.getConfig().DispatchServerPublicIp) + "/gacha"; + String record = "http://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + "/gacha"; GachaInfo.Builder info = GachaInfo.newBuilder() .setGachaType(this.getGachaType()) diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index a38b91812..079f0dda3 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -299,7 +299,7 @@ public class GachaManager { @Subscribe public synchronized void watchBannerJson(GameServerTickEvent tickEvent) { - if(Grasscutter.getConfig().getServerOptions().WatchGacha) { + if(Grasscutter.getConfig().getGameServerOptions().WatchGacha) { try { WatchKey watchKey = watchService.take(); diff --git a/src/main/java/emu/grasscutter/game/inventory/Inventory.java b/src/main/java/emu/grasscutter/game/inventory/Inventory.java index 19bdcc525..8954b1031 100644 --- a/src/main/java/emu/grasscutter/game/inventory/Inventory.java +++ b/src/main/java/emu/grasscutter/game/inventory/Inventory.java @@ -37,10 +37,10 @@ public class Inventory implements Iterable { this.store = new Long2ObjectOpenHashMap<>(); this.inventoryTypes = new Int2ObjectOpenHashMap<>(); - this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitWeapon)); - this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitRelic)); - this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitMaterial)); - this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(Grasscutter.getConfig().getServerOptions().InventoryLimitFurniture)); + this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitWeapon)); + this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitRelic)); + this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitMaterial)); + this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitFurniture)); } public GenshinPlayer getPlayer() { diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index f13b9dc2f..cda908480 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -55,7 +55,7 @@ public final class DispatchServer { public static String query_cur_region = ""; public DispatchServer() { - this.address = new InetSocketAddress(Grasscutter.getConfig().DispatchServerIp, Grasscutter.getConfig().DispatchServerPort); + this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().Port); this.gson = new GsonBuilder().create(); this.loadQueries(); @@ -99,19 +99,19 @@ public final class DispatchServer { byte[] decoded2 = Base64.getDecoder().decode(query_cur_region); QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2); - + RegionSimpleInfo server = RegionSimpleInfo.newBuilder() .setName("os_usa") - .setTitle(Grasscutter.getConfig().GameServerName) + .setTitle(Grasscutter.getConfig().getGameServerOptions().Name) .setType("DEV_PUBLIC") - .setDispatchUrl("https://" + (Grasscutter.getConfig().DispatchServerPublicIp.isEmpty() ? Grasscutter.getConfig().DispatchServerIp : Grasscutter.getConfig().DispatchServerPublicIp) + ":" + getAddress().getPort() + "/query_cur_region") + .setDispatchUrl("https://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region") .build(); RegionSimpleInfo serverTest2 = RegionSimpleInfo.newBuilder() .setName("os_euro") .setTitle("Grasscutter") .setType("DEV_PUBLIC") - .setDispatchUrl("https://" + (Grasscutter.getConfig().DispatchServerPublicIp.isEmpty() ? Grasscutter.getConfig().DispatchServerIp : Grasscutter.getConfig().DispatchServerPublicIp) + ":" + getAddress().getPort() + "/query_cur_region") + .setDispatchUrl("https://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region") .build(); QueryRegionListHttpRsp regionList = QueryRegionListHttpRsp.newBuilder() @@ -123,8 +123,8 @@ public final class DispatchServer { .build(); RegionInfo currentRegion = regionQuery.getRegionInfo().toBuilder() - .setIp((Grasscutter.getConfig().GameServerPublicIp.isEmpty() ? Grasscutter.getConfig().GameServerIp : Grasscutter.getConfig().GameServerPublicIp)) - .setPort(Grasscutter.getConfig().GameServerPort) + .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp)) + .setPort(Grasscutter.getConfig().getGameServerOptions().Port) .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) .build(); @@ -140,12 +140,12 @@ public final class DispatchServer { public void start() throws Exception { HttpServer server; - if(Grasscutter.getConfig().UseSSL) { + if(Grasscutter.getConfig().getDispatchOptions().UseSSL) { HttpsServer httpsServer; httpsServer = HttpsServer.create(getAddress(), 0); SSLContext sslContext = SSLContext.getInstance("TLS"); - try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().DispatchServerKeystorePath)) { - char[] keystorePassword = Grasscutter.getConfig().DispatchServerKeystorePassword.toCharArray(); + try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) { + char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray(); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(fis, keystorePassword); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); @@ -228,7 +228,7 @@ public final class DispatchServer { // Check if account exists, else create a new one. if (account == null) { // Account doesnt exist, so we can either auto create it if the config value is set - if (Grasscutter.getConfig().ServerOptions.AutomaticallyCreateAccounts) { + if (Grasscutter.getConfig().getDispatchOptions().AutomaticallyCreateAccounts) { // This account has been created AUTOMATICALLY. There will be no permissions added. account = DatabaseHelper.createAccountWithId(requestData.account, 0); @@ -410,7 +410,7 @@ public final class DispatchServer { Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort()); // Logging servers - HttpServer overseaLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().DispatchServerIp, 8888), 0); + HttpServer overseaLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, 8888), 0); overseaLogServer.createContext( // overseauspider.yuanshen.com "/log", new DispatchHttpJsonHandler("{\"code\":0}") @@ -418,7 +418,7 @@ public final class DispatchServer { overseaLogServer.start(); Grasscutter.getLogger().info("Log server (overseauspider) started on port " + 8888); - HttpServer uploadLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().DispatchServerIp, Grasscutter.getConfig().UploadLogPort), 0); + HttpServer uploadLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().UploadLogPort), 0); uploadLogServer.createContext( // log-upload-os.mihoyo.com "/crash/dataUpload", new DispatchHttpJsonHandler("{\"code\":0}") @@ -435,7 +435,7 @@ public final class DispatchServer { os.close(); }); uploadLogServer.start(); - Grasscutter.getLogger().info("Log server (log-upload-os) started on port " + Grasscutter.getConfig().UploadLogPort); + Grasscutter.getLogger().info("Log server (log-upload-os) started on port " + Grasscutter.getConfig().getDispatchOptions().UploadLogPort); } private Map parseQueryString(String qs) { diff --git a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java index 118637668..62a57df91 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java +++ b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java @@ -87,7 +87,7 @@ public class GameServerPacketHandler { } // Log unhandled packets - if (Grasscutter.getConfig().LOG_PACKETS) { + if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) { //Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + PacketOpcodesUtil.getOpcodeName(opcode)); } } diff --git a/src/main/java/emu/grasscutter/server/game/GameSession.java b/src/main/java/emu/grasscutter/server/game/GameSession.java index 1a54f31ba..ebd66dc20 100644 --- a/src/main/java/emu/grasscutter/server/game/GameSession.java +++ b/src/main/java/emu/grasscutter/server/game/GameSession.java @@ -165,7 +165,7 @@ public class GameSession extends MihoyoKcpChannel { byte[] data = genshinPacket.build(); // Log - if (Grasscutter.getConfig().LOG_PACKETS) { + if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) { logPacket(genshinPacket); } @@ -225,7 +225,7 @@ public class GameSession extends MihoyoKcpChannel { } // Log packet - if (Grasscutter.getConfig().LOG_PACKETS) { + if (Grasscutter.getConfig().getGameServerOptions().LOG_PACKETS) { Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")"); System.out.println(Utils.bytesToHex(payload)); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java index c21a15351..a3309a5c1 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java @@ -19,7 +19,7 @@ public class PacketPlayerStoreNotify extends GenshinPacket { PlayerStoreNotify.Builder p = PlayerStoreNotify.newBuilder() .setStoreType(StoreType.StorePack) - .setWeightLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitAll); + .setWeightLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitAll); for (GenshinItem item : player.getInventory()) { Item itemProto = item.toProto(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java index 8048e4b0d..5af6999d5 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java @@ -1,6 +1,6 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.Config.ServerOptions; +import emu.grasscutter.Config.GameServerOptions; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; import emu.grasscutter.game.GenshinPlayer; @@ -14,7 +14,7 @@ public class PacketPullRecentChatRsp extends GenshinPacket { public PacketPullRecentChatRsp(GenshinPlayer player) { super(PacketOpcodes.PullRecentChatRsp); - ServerOptions serverOptions = Grasscutter.getConfig().getServerOptions(); + GameServerOptions serverOptions = Grasscutter.getConfig().getGameServerOptions(); PullRecentChatRsp.Builder proto = PullRecentChatRsp.newBuilder(); if (serverOptions.WelcomeEmotes != null && serverOptions.WelcomeEmotes.length > 0) { @@ -33,7 +33,7 @@ public class PacketPullRecentChatRsp extends GenshinPacket { .setTime((int) (System.currentTimeMillis() / 1000)) .setUid(GenshinConstants.SERVER_CONSOLE_UID) .setToUid(player.getUid()) - .setText(Grasscutter.getConfig().getServerOptions().WelcomeMotd) + .setText(Grasscutter.getConfig().getGameServerOptions().WelcomeMotd) .build(); proto.addChatInfo(welcomeMotd); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java index b93130388..04668b00e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java @@ -13,11 +13,11 @@ public class PacketStoreWeightLimitNotify extends GenshinPacket { StoreWeightLimitNotify p = StoreWeightLimitNotify.newBuilder() .setStoreType(StoreType.StorePack) - .setWeightLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitAll) - .setWeaponCountLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitWeapon) - .setReliquaryCountLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitRelic) - .setMaterialCountLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitMaterial) - .setFurnitureCountLimit(Grasscutter.getConfig().getServerOptions().InventoryLimitFurniture) + .setWeightLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitAll) + .setWeaponCountLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitWeapon) + .setReliquaryCountLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitRelic) + .setMaterialCountLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitMaterial) + .setFurnitureCountLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitFurniture) .build(); this.setData(p); From 1b1e272dacf44c4bb04f4224ce605848d9ee53e6 Mon Sep 17 00:00:00 2001 From: labalityowo <56186498+labalityowo@users.noreply.github.com> Date: Thu, 21 Apr 2022 14:01:51 +0700 Subject: [PATCH 53/91] Fix Ayaka and weapon banner titles not showing up --- data/Banners.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/Banners.json b/data/Banners.json index 959a8b436..a4f724ac9 100644 --- a/data/Banners.json +++ b/data/Banners.json @@ -19,7 +19,7 @@ "bannerType": "EVENT", "prefabPath": "GachaShowPanel_A079", "previewPrefabPath": "UI_Tab_GachaShowPanel_A079", - "titlePath": "UI_GACHA_SHOW_PANEL_A079_TITLE", + "titlePath": "UI_GACHA_SHOW_PANEL_A048_TITLE", "costItem": 223, "beginTime": 0, "endTime": 1924992000, @@ -34,7 +34,7 @@ "bannerType": "WEAPON", "prefabPath": "GachaShowPanel_A080", "previewPrefabPath": "UI_Tab_GachaShowPanel_A080", - "titlePath": "UI_GACHA_SHOW_PANEL_A080_TITLE", + "titlePath": "UI_GACHA_SHOW_PANEL_A021_TITLE", "costItem": 223, "beginTime": 0, "endTime": 1924992000, From 29f6d77e2f83e7d3c14f21ce2dadac12c7a45cdb Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Thu, 21 Apr 2022 00:28:28 -0700 Subject: [PATCH 54/91] Add config entry for OverseaLogPort (default: 8888) --- src/main/java/emu/grasscutter/Config.java | 1 + .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index dddbbfa9e..4d4cf4a38 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -13,6 +13,7 @@ public final class Config { public String GameServerPublicIp = ""; public int GameServerPort = 22102; + public int OverseaLogPort = 8888; public int UploadLogPort = 80; public String DatabaseUrl = "mongodb://localhost:27017"; diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index f13b9dc2f..6b4336858 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -410,7 +410,7 @@ public final class DispatchServer { Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort()); // Logging servers - HttpServer overseaLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().DispatchServerIp, 8888), 0); + HttpServer overseaLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().DispatchServerIp, Grasscutter.getConfig().OverseaLogPort), 0); overseaLogServer.createContext( // overseauspider.yuanshen.com "/log", new DispatchHttpJsonHandler("{\"code\":0}") From bc07fa77b207398f1f2dfab585f5bdf4916a916c Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Thu, 21 Apr 2022 00:29:06 -0700 Subject: [PATCH 55/91] Make sure SetEntityClientDataNotify is a valid proto before replaying it to the other players --- .../packet/recv/HandlerSetEntityClientDataNotify.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java index 1a60f677b..d194e1465 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java @@ -3,6 +3,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SetEntityClientDataNotifyOuterClass.SetEntityClientDataNotify; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; @@ -16,8 +17,11 @@ public class HandlerSetEntityClientDataNotify extends PacketHandler { return; } + // Make sure packet is a valid proto before replaying it to the other players + SetEntityClientDataNotify notif = SetEntityClientDataNotify.parseFrom(payload); + GenshinPacket packet = new GenshinPacket(PacketOpcodes.SetEntityClientDataNotify, true); - packet.setData(payload); + packet.setData(notif); session.getPlayer().getScene().broadcastPacketToOthers(session.getPlayer(), packet); } From 9ce436022d84a8856a77463ec504604a8ec25fbb Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 18:04:00 +0800 Subject: [PATCH 56/91] Multi-server dispatch support and server run modes --- src/main/java/emu/grasscutter/Config.java | 19 +++ .../java/emu/grasscutter/Grasscutter.java | 32 +++- .../grasscutter/database/DatabaseHelper.java | 12 +- .../grasscutter/database/DatabaseManager.java | 43 ++++- .../server/dispatch/DispatchServer.java | 161 ++++++++++++------ .../packet/send/PacketPlayerLoginRsp.java | 2 +- 6 files changed, 198 insertions(+), 71 deletions(-) diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index c6748fb41..938cbec77 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -1,5 +1,7 @@ package emu.grasscutter; +import java.util.ArrayList; + public final class Config { public String DatabaseUrl = "mongodb://localhost:27017"; @@ -11,6 +13,7 @@ public final class Config { public String DUMPS_FOLDER = "./dumps/"; public String KEY_FOLDER = "./keys/"; + public String RunMode = "HYBRID"; // HYBRID, DISPATCH_ONLY, GAME_ONLY public GameServerOptions GameServer = new GameServerOptions(); public DispatchServerOptions DispatchServer = new DispatchServerOptions(); @@ -30,6 +33,19 @@ public final class Config { public Boolean UseSSL = true; public boolean AutomaticallyCreateAccounts = false; + + public RegionInfo[] GameServers = {}; + + public RegionInfo[] getGameServers() { + return GameServers; + } + + public static class RegionInfo { + public String Name = "os_usa"; + public String Title = "Test"; + public String Ip = "127.0.0.1"; + public int Port = 22102; + } } public static class GameServerOptions { @@ -38,6 +54,9 @@ public final class Config { public String PublicIp = ""; public int Port = 22102; + public String DispatchServerDatabaseUrl = "mongodb://localhost:27017"; + public String DispatchServerDatabaseCollection = "grasscutter"; + public boolean LOG_PACKETS = false; public int InventoryLimitWeapon = 2000; diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 0b8f1efd8..614b67ca1 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -73,11 +73,26 @@ public final class Grasscutter { DatabaseManager.initialize(); // Start servers. - dispatchServer = new DispatchServer(); - dispatchServer.start(); - - gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port)); - gameServer.start(); + if(getConfig().RunMode.equalsIgnoreCase("HYBRID")) { + dispatchServer = new DispatchServer(); + dispatchServer.start(); + + gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port)); + gameServer.start(); + } else if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) { + dispatchServer = new DispatchServer(); + dispatchServer.start(); + } else if(getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { + gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port)); + gameServer.start(); + } else { + getLogger().error("Invalid server run mode. " + getConfig().RunMode); + getLogger().error("Server run mode must be 'HYBRID', 'DISPATCH_ONLY', or 'GAME_ONLY'. Unable to start Grasscutter..."); + getLogger().error("Shutting down..."); + System.exit(1); + } + + // Open console. startConsole(); @@ -104,9 +119,14 @@ public final class Grasscutter { try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { while ((input = br.readLine()) != null) { try { + if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) { + getLogger().error("Commands are not supported in dispatch only mode"); + return; + } CommandMap.getInstance().invoke(null, input); } catch (Exception e) { - Grasscutter.getLogger().error("Command error: " + e.getMessage()); + Grasscutter.getLogger().error("Command error: "); + e.printStackTrace(); } } } catch (Exception e) { diff --git a/src/main/java/emu/grasscutter/database/DatabaseHelper.java b/src/main/java/emu/grasscutter/database/DatabaseHelper.java index 0289c3e71..ed092e976 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseHelper.java +++ b/src/main/java/emu/grasscutter/database/DatabaseHelper.java @@ -74,36 +74,36 @@ public class DatabaseHelper { } public static void saveAccount(Account account) { - DatabaseManager.getDatastore().save(account); + DatabaseManager.getAccountDatastore().save(account); } public static Account getAccountByName(String username) { - MorphiaCursor cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username).find(FIND_ONE); + MorphiaCursor cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username).find(FIND_ONE); if (!cursor.hasNext()) return null; return cursor.next(); } public static Account getAccountByToken(String token) { if (token == null) return null; - MorphiaCursor cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("token").equal(token).find(FIND_ONE); + MorphiaCursor cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("token").equal(token).find(FIND_ONE); if (!cursor.hasNext()) return null; return cursor.next(); } public static Account getAccountById(String uid) { - MorphiaCursor cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("_id").equal(uid).find(FIND_ONE); + MorphiaCursor cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("_id").equal(uid).find(FIND_ONE); if (!cursor.hasNext()) return null; return cursor.next(); } public static Account getAccountByPlayerId(int playerId) { - MorphiaCursor cursor = DatabaseManager.getDatastore().createQuery(Account.class).field("playerId").equal(playerId).find(FIND_ONE); + MorphiaCursor cursor = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("playerId").equal(playerId).find(FIND_ONE); if (!cursor.hasNext()) return null; return cursor.next(); } public static boolean deleteAccount(String username) { - Query q = DatabaseManager.getDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username); + Query q = DatabaseManager.getAccountDatastore().createQuery(Account.class).field("username").equalIgnoreCase(username); return DatabaseManager.getDatastore().findAndDelete(q) != null; } diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 97e27a81a..530581517 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -17,7 +17,10 @@ import emu.grasscutter.game.inventory.GenshinItem; public final class DatabaseManager { private static MongoClient mongoClient; + private static MongoClient dispatchMongoClient; + private static Datastore datastore; + private static Datastore dispatchDatastore; private static final Class[] mappedClasses = new Class[] { DatabaseCounter.class, Account.class, GenshinPlayer.class, GenshinAvatar.class, GenshinItem.class, Friendship.class @@ -26,18 +29,28 @@ public final class DatabaseManager { public static MongoClient getMongoClient() { return mongoClient; } - - public static Datastore getDatastore() { - return datastore; - } - - public static MongoDatabase getDatabase() { + + public static Datastore getDatastore() { + return datastore; + } + + public static MongoDatabase getDatabase() { return getDatastore().getDatabase(); } + + // Yes. I very dislike this method also but I'm lazy. Probably replace it by making the game server connect to the dispatch server instead. + public static Datastore getAccountDatastore() { + if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { + return dispatchDatastore; + } else { + return datastore; + } + } public static void initialize() { // Initialize mongoClient = new MongoClient(new MongoClientURI(Grasscutter.getConfig().DatabaseUrl)); + dispatchMongoClient = new MongoClient(new MongoClientURI(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl)); Morphia morphia = new Morphia(); // TODO Update when migrating to Morphia 2.0 @@ -50,6 +63,7 @@ public final class DatabaseManager { // Build datastore datastore = morphia.createDatastore(mongoClient, Grasscutter.getConfig().DatabaseCollection); + dispatchDatastore = morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection); // Ensure indexes try { @@ -67,6 +81,23 @@ public final class DatabaseManager { datastore.ensureIndexes(); } } + + // Ensure indexes for dispatch server + try { + dispatchDatastore.ensureIndexes(); + } catch (MongoCommandException e) { + Grasscutter.getLogger().info("Mongo index error: ", e); + // Duplicate index error + if (e.getCode() == 85) { + // Drop all indexes and re add them + MongoIterable collections = dispatchDatastore.getDatabase().listCollectionNames(); + for (String name : collections) { + dispatchDatastore.getDatabase().getCollection(name).dropIndexes(); + } + // Add back indexes + dispatchDatastore.ensureIndexes(); + } + } } public static synchronized int getNextId(Class c) { diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index cda908480..53699b05b 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -9,10 +9,7 @@ import java.net.InetSocketAddress; import java.net.URI; import java.net.URLDecoder; import java.security.KeyStore; -import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; @@ -25,6 +22,7 @@ import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsServer; +import emu.grasscutter.Config; import emu.grasscutter.Grasscutter; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; @@ -46,15 +44,18 @@ import com.sun.net.httpserver.HttpServer; public final class DispatchServer { private final InetSocketAddress address; private final Gson gson; - private QueryCurrRegionHttpRsp currRegion; + //private QueryCurrRegionHttpRsp currRegion; public String regionListBase64; - public String regionCurrentBase64; - + public HashMap regions; + public HashMap usersIngame; + public static String query_region_list = ""; public static String query_cur_region = ""; - + public DispatchServer() { + this.regions = new HashMap(); + this.usersIngame = new HashMap(); this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().Port); this.gson = new GsonBuilder().create(); @@ -70,8 +71,13 @@ public final class DispatchServer { return gson; } - public QueryCurrRegionHttpRsp getCurrRegion() { - return currRegion; + public QueryCurrRegionHttpRsp getCurrRegion(InetSocketAddress address) { + if(usersIngame.containsKey(address)) { + return regions.get(usersIngame.get(address)).parsedRegionQuery; + } + + Grasscutter.getLogger().error("User is not logged in to dispatch server. " + address.getAddress() + ":" + address.getPort()); + return null; } public void loadQueries() { @@ -100,39 +106,67 @@ public final class DispatchServer { byte[] decoded2 = Base64.getDecoder().decode(query_cur_region); QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2); - RegionSimpleInfo server = RegionSimpleInfo.newBuilder() - .setName("os_usa") - .setTitle(Grasscutter.getConfig().getGameServerOptions().Name) - .setType("DEV_PUBLIC") - .setDispatchUrl("https://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region") - .build(); - - RegionSimpleInfo serverTest2 = RegionSimpleInfo.newBuilder() - .setName("os_euro") - .setTitle("Grasscutter") - .setType("DEV_PUBLIC") - .setDispatchUrl("https://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region") - .build(); - + List servers = new ArrayList(); + List usedNames = new ArrayList(); // List to check for potential naming conflicts + if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in hybrid mode + String defaultServerName = "os_usa"; + RegionSimpleInfo server = RegionSimpleInfo.newBuilder() + .setName("os_usa") + .setTitle(Grasscutter.getConfig().getGameServerOptions().Name) + .setType("DEV_PUBLIC") + .setDispatchUrl("https://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region_" + defaultServerName) + .build(); + usedNames.add(defaultServerName); + servers.add(server); + + RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() + .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp)) + .setPort(Grasscutter.getConfig().getGameServerOptions().Port) + .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) + .build(); + + QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build(); + regions.put(defaultServerName, new RegionData(parsedRegionQuery, Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()))); + + } else { + if(Grasscutter.getConfig().getDispatchOptions().getGameServers().length == 0) { + Grasscutter.getLogger().error("Dispatch server has no game servers available. Exiting due to unplayable state."); + System.exit(1); + } + } + + for (Config.DispatchServerOptions.RegionInfo regionInfo : Grasscutter.getConfig().getDispatchOptions().getGameServers()) { + if(usedNames.contains(regionInfo.Name)) { + Grasscutter.getLogger().error("Region name already in use."); + continue; + } + RegionSimpleInfo server = RegionSimpleInfo.newBuilder() + .setName(regionInfo.Name) + .setTitle(regionInfo.Title) + .setType("DEV_PUBLIC") + .setDispatchUrl("https://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region_" + regionInfo.Name) + .build(); + usedNames.add(regionInfo.Name); + servers.add(server); + + RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() + .setIp(regionInfo.Ip) + .setPort(regionInfo.Port) + .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) + .build(); + + QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build(); + regions.put(regionInfo.Name, new RegionData(parsedRegionQuery, Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()))); + } + QueryRegionListHttpRsp regionList = QueryRegionListHttpRsp.newBuilder() - .addServers(server) - .addServers(serverTest2) + .addAllServers(servers) .setClientSecretKey(rl.getClientSecretKey()) .setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted()) .setEnableLoginPc(true) .build(); - - RegionInfo currentRegion = regionQuery.getRegionInfo().toBuilder() - .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp)) - .setPort(Grasscutter.getConfig().getGameServerOptions().Port) - .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) - .build(); - - QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(currentRegion).build(); this.regionListBase64 = Base64.getEncoder().encodeToString(regionList.toByteString().toByteArray()); - this.regionCurrentBase64 = Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()); - this.currRegion = parsedRegionQuery; } catch (Exception e) { Grasscutter.getLogger().error("Error while initializing region info!", e); } @@ -188,24 +222,36 @@ public final class DispatchServer { OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); - }); - server.createContext("/query_cur_region", t -> { - // Log - Grasscutter.getLogger().info("Client request: query_cur_region"); - // Create a response form the request query parameters - URI uri = t.getRequestURI(); - String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="; - if (uri.getQuery() != null && uri.getQuery().length() > 0) { - response = regionCurrentBase64; + + if(usersIngame.containsKey(t.getRemoteAddress())) { + usersIngame.remove(t.getRemoteAddress()); } - // Set the response header status and length - t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8")); - t.sendResponseHeaders(200, response.getBytes().length); - // Write the response string - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); }); + + for (String regionName : regions.keySet()) { + server.createContext("/query_cur_region_" + regionName, t -> { + String regionCurrentBase64 = regions.get(regionName).Base64; + + // Log + Grasscutter.getLogger().info("Client request: query_cur_region_" + regionName); + // Create a response form the request query parameters + URI uri = t.getRequestURI(); + String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="; + if (uri.getQuery() != null && uri.getQuery().length() > 0) { + response = regionCurrentBase64; + } + // Set the response header status and length + t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8")); + t.sendResponseHeaders(200, response.getBytes().length); + // Write the response string + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + //Save region info to hashmap for user, this for getCurrRegion(); + usersIngame.put(t.getRemoteAddress(), regionName); + }); + } + // Login via account server.createContext("/hk4e_global/mdk/shield/api/login", t -> { // Get post data @@ -464,4 +510,15 @@ public final class DispatchServer { } return result; } + + public static class RegionData { + + QueryCurrRegionHttpRsp parsedRegionQuery; + String Base64; + + public RegionData(QueryCurrRegionHttpRsp prq, String b64) { + this.parsedRegionQuery = prq; + this.Base64 = b64; + } + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java index 0d0871ca9..eef1a03e6 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java @@ -14,7 +14,7 @@ public class PacketPlayerLoginRsp extends GenshinPacket { this.setUseDispatchKey(true); - RegionInfo info = Grasscutter.getDispatchServer().getCurrRegion().getRegionInfo(); + RegionInfo info = Grasscutter.getDispatchServer().getCurrRegion(session.getAddress()).getRegionInfo(); PlayerLoginRsp p = PlayerLoginRsp.newBuilder() .setIsUseAbilityHash(true) // true From 127559274762356f05b7b5e1aefb14b032cba523 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 19:20:49 +0800 Subject: [PATCH 57/91] Fixed null pointer exception in hybrid mode. --- .../server/dispatch/DispatchServer.java | 27 +++++++++---------- .../packet/send/PacketPlayerLoginRsp.java | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 6b2661def..37ed6c5ac 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -5,6 +5,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; import java.net.URLDecoder; @@ -44,18 +45,18 @@ import com.sun.net.httpserver.HttpServer; public final class DispatchServer { private final InetSocketAddress address; private final Gson gson; + private final String defaultServerName = "os_usa"; //private QueryCurrRegionHttpRsp currRegion; public String regionListBase64; public HashMap regions; - public HashMap usersIngame; + public HashMap usersConnected; public static String query_region_list = ""; public static String query_cur_region = ""; public DispatchServer() { this.regions = new HashMap(); - this.usersIngame = new HashMap(); this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().Port); this.gson = new GsonBuilder().create(); @@ -71,12 +72,13 @@ public final class DispatchServer { return gson; } - public QueryCurrRegionHttpRsp getCurrRegion(InetSocketAddress address) { - if(usersIngame.containsKey(address)) { - return regions.get(usersIngame.get(address)).parsedRegionQuery; + public QueryCurrRegionHttpRsp getCurrRegion() { + // Needs to be fixed by having the game servers connect to the dispatch server. + if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { + return regions.get(defaultServerName).parsedRegionQuery; } - Grasscutter.getLogger().error("User is not logged in to dispatch server. " + address.getAddress() + ":" + address.getPort()); + Grasscutter.getLogger().error("Ignore the error below"); return null; } @@ -109,7 +111,6 @@ public final class DispatchServer { List servers = new ArrayList(); List usedNames = new ArrayList(); // List to check for potential naming conflicts if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in hybrid mode - String defaultServerName = "os_usa"; RegionSimpleInfo server = RegionSimpleInfo.newBuilder() .setName("os_usa") .setTitle(Grasscutter.getConfig().getGameServerOptions().Name) @@ -222,16 +223,11 @@ public final class DispatchServer { OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); - - if(usersIngame.containsKey(t.getRemoteAddress())) { - usersIngame.remove(t.getRemoteAddress()); - } }); for (String regionName : regions.keySet()) { server.createContext("/query_cur_region_" + regionName, t -> { String regionCurrentBase64 = regions.get(regionName).Base64; - // Log Grasscutter.getLogger().info("Client request: query_cur_region_" + regionName); // Create a response form the request query parameters @@ -247,8 +243,6 @@ public final class DispatchServer { OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); - //Save region info to hashmap for user, this for getCurrRegion(); - usersIngame.put(t.getRemoteAddress(), regionName); }); } @@ -521,4 +515,9 @@ public final class DispatchServer { this.Base64 = b64; } } + + public static class UserConnnectionData { + public InetAddress dispatchConnectedAddress; + public String connectedRegion; + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java index eef1a03e6..0d0871ca9 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java @@ -14,7 +14,7 @@ public class PacketPlayerLoginRsp extends GenshinPacket { this.setUseDispatchKey(true); - RegionInfo info = Grasscutter.getDispatchServer().getCurrRegion(session.getAddress()).getRegionInfo(); + RegionInfo info = Grasscutter.getDispatchServer().getCurrRegion().getRegionInfo(); PlayerLoginRsp p = PlayerLoginRsp.newBuilder() .setIsUseAbilityHash(true) // true From b38d5b19bb54c4c98344c844ef9cf2d9007a5070 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Thu, 21 Apr 2022 21:41:07 +0800 Subject: [PATCH 58/91] Add port & SSL options to proxy script Signed-off-by: Jaida Wu --- proxy.py | 9 ++++++++- proxy_config.py | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/proxy.py b/proxy.py index 2d2282af9..86145cde6 100644 --- a/proxy.py +++ b/proxy.py @@ -16,12 +16,14 @@ # - mitmdump from mitmproxy # # @author MlgmXyysd -# @version 1.0 +# @version 1.1 # ## from mitmproxy import http +from proxy_config import USE_SSL from proxy_config import REMOTE_HOST +from proxy_config import REMOTE_PORT class MlgmXyysd_Genshin_Impact_Proxy: @@ -60,7 +62,12 @@ class MlgmXyysd_Genshin_Impact_Proxy: def request(self, flow: http.HTTPFlow) -> None: if flow.request.host in self.LIST_DOMAINS: + if USE_SSL: + flow.request.scheme = "https" + else: + flow.request.scheme = "http" flow.request.host = REMOTE_HOST + flow.request.port = REMOTE_PORT addons = [ MlgmXyysd_Genshin_Impact_Proxy() diff --git a/proxy_config.py b/proxy_config.py index f048ef88c..5025a974e 100644 --- a/proxy_config.py +++ b/proxy_config.py @@ -1,2 +1,4 @@ # This can also be replaced with another IP address. -REMOTE_HOST = "localhost" \ No newline at end of file +USE_SSL = True +REMOTE_HOST = "127.0.0.1" +REMOTE_PORT = 443 \ No newline at end of file From e242e72b2d9a09b22abc1f9769235a00e84f4f94 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Thu, 21 Apr 2022 21:41:30 +0800 Subject: [PATCH 59/91] Drop independent log server Signed-off-by: Jaida Wu --- src/main/java/emu/grasscutter/Config.java | 3 --- .../server/dispatch/DispatchServer.java | 18 ++++++------------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index 4d4cf4a38..65ada3969 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -13,9 +13,6 @@ public final class Config { public String GameServerPublicIp = ""; public int GameServerPort = 22102; - public int OverseaLogPort = 8888; - public int UploadLogPort = 80; - public String DatabaseUrl = "mongodb://localhost:27017"; public String DatabaseCollection = "grasscutter"; diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 6b4336858..e59f9d251 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -405,25 +405,18 @@ public final class DispatchServer { "/perf/config/verify", new DispatchHttpJsonHandler("{\"code\":0}") ); - // Start server - server.start(); - Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort()); // Logging servers - HttpServer overseaLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().DispatchServerIp, Grasscutter.getConfig().OverseaLogPort), 0); - overseaLogServer.createContext( // overseauspider.yuanshen.com + server.createContext( // overseauspider.yuanshen.com "/log", new DispatchHttpJsonHandler("{\"code\":0}") ); - overseaLogServer.start(); - Grasscutter.getLogger().info("Log server (overseauspider) started on port " + 8888); - HttpServer uploadLogServer = HttpServer.create(new InetSocketAddress(Grasscutter.getConfig().DispatchServerIp, Grasscutter.getConfig().UploadLogPort), 0); - uploadLogServer.createContext( // log-upload-os.mihoyo.com + server.createContext( // log-upload-os.mihoyo.com "/crash/dataUpload", new DispatchHttpJsonHandler("{\"code\":0}") ); - uploadLogServer.createContext("/gacha", t -> { + server.createContext("/gacha", t -> { //Create a response form the request query parameters String response = "Gacha"; //Set the response header status and length @@ -434,8 +427,9 @@ public final class DispatchServer { os.write(response.getBytes()); os.close(); }); - uploadLogServer.start(); - Grasscutter.getLogger().info("Log server (log-upload-os) started on port " + Grasscutter.getConfig().UploadLogPort); + // Start server + server.start(); + Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort()); } private Map parseQueryString(String qs) { From 77b33377d8377b3e37a3addfd159a907b5da6e2f Mon Sep 17 00:00:00 2001 From: xmplay <81370285+xmplay@users.noreply.github.com> Date: Thu, 21 Apr 2022 15:04:47 +0100 Subject: [PATCH 60/91] bring back the jade chamber and other old scene tags --- .../server/packet/send/PacketSceneUnlockInfoNotify.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java index fb45bb361..bd1b30685 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java @@ -12,7 +12,7 @@ public class PacketSceneUnlockInfoNotify extends GenshinPacket { SceneUnlockInfoNotify proto = SceneUnlockInfoNotify.newBuilder() .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(1)) - .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(3)) + .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(3).addSceneTagIdList(102).addSceneTagIdList(113).addSceneTagIdList(117)) .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(4).addSceneTagIdList(106).addSceneTagIdList(109)) .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(5)) .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(6)) From bce3e256cfa783ebd6354a43250f4acc617cfc9c Mon Sep 17 00:00:00 2001 From: zamlty <29914613+zamlty@users.noreply.github.com> Date: Thu, 21 Apr 2022 22:21:39 +0800 Subject: [PATCH 61/91] Fix artifact & weapon generation by give command --- .../java/emu/grasscutter/command/commands/GiveCommand.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index 7d15a4759..a533130bc 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -95,18 +95,19 @@ public final class GiveCommand implements CommandHandler { } private void item(GenshinPlayer player, ItemData itemData, int amount) { - GenshinItem genshinItem = new GenshinItem(itemData); if (itemData.isEquip()) { List items = new LinkedList<>(); for (int i = 0; i < amount; i++) { - items.add(genshinItem); + items.add(new GenshinItem(itemData)); } player.getInventory().addItems(items); player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); } else { + GenshinItem genshinItem = new GenshinItem(itemData); genshinItem.setCount(amount); player.getInventory().addItem(genshinItem); player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop)); } } } + From 77fcce6510a55f52297220d178e28a11cd391393 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 22:38:01 +0800 Subject: [PATCH 62/91] Fixed null pointer exception in game only mode PacketPlayerLoginRsp now creates the required QuerryCurrRegionHttpRsp itself. --- .../packet/send/PacketPlayerLoginRsp.java | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java index 0d0871ca9..a5e167100 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java @@ -1,21 +1,61 @@ package emu.grasscutter.server.packet.send; +import com.google.protobuf.ByteString; import emu.grasscutter.Grasscutter; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.PlayerLoginRspOuterClass.PlayerLoginRsp; +import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass; import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.utils.FileUtils; + +import java.io.File; +import java.net.URL; +import java.util.Base64; public class PacketPlayerLoginRsp extends GenshinPacket { + private static QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp regionCache; + public PacketPlayerLoginRsp(GameSession session) { super(PacketOpcodes.PlayerLoginRsp, 1); this.setUseDispatchKey(true); - - RegionInfo info = Grasscutter.getDispatchServer().getCurrRegion().getRegionInfo(); - + + RegionInfo info; + + if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { + if (regionCache == null) { + try { + File file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt"); + String query_cur_region = ""; + if (file.exists()) { + query_cur_region = new String(FileUtils.read(file)); + } else { + Grasscutter.getLogger().warn("query_cur_region not found! Using default current region."); + } + + byte[] decodedCurRegion = Base64.getDecoder().decode(query_cur_region); + QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.parseFrom(decodedCurRegion); + + RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() + .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp)) + .setPort(Grasscutter.getConfig().getGameServerOptions().Port) + .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) + .build(); + + regionCache = regionQuery.toBuilder().setRegionInfo(serverRegion).build(); + } catch (Exception e) { + Grasscutter.getLogger().error("Error while initializing region cache!", e); + } + } + + info = regionCache.getRegionInfo(); + } else { + info = Grasscutter.getDispatchServer().getCurrRegion().getRegionInfo(); + } + PlayerLoginRsp p = PlayerLoginRsp.newBuilder() .setIsUseAbilityHash(true) // true .setAbilityHashCode(1844674) // 1844674 From 1969d38e06bbda9b04f438ea1f1bb672812bbe1a Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 22:50:06 +0800 Subject: [PATCH 63/91] DatabaseManager clean up --- .../grasscutter/database/DatabaseManager.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 530581517..46dad537d 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -38,7 +38,8 @@ public final class DatabaseManager { return getDatastore().getDatabase(); } - // Yes. I very dislike this method also but I'm lazy. Probably replace it by making the game server connect to the dispatch server instead. + // Yes. I very dislike this method. However, this will be good for now. + // TODO: Add dispatch routes for player account management public static Datastore getAccountDatastore() { if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { return dispatchDatastore; @@ -50,7 +51,6 @@ public final class DatabaseManager { public static void initialize() { // Initialize mongoClient = new MongoClient(new MongoClientURI(Grasscutter.getConfig().DatabaseUrl)); - dispatchMongoClient = new MongoClient(new MongoClientURI(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl)); Morphia morphia = new Morphia(); // TODO Update when migrating to Morphia 2.0 @@ -63,7 +63,6 @@ public final class DatabaseManager { // Build datastore datastore = morphia.createDatastore(mongoClient, Grasscutter.getConfig().DatabaseCollection); - dispatchDatastore = morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection); // Ensure indexes try { @@ -82,20 +81,25 @@ public final class DatabaseManager { } } - // Ensure indexes for dispatch server - try { - dispatchDatastore.ensureIndexes(); - } catch (MongoCommandException e) { - Grasscutter.getLogger().info("Mongo index error: ", e); - // Duplicate index error - if (e.getCode() == 85) { - // Drop all indexes and re add them - MongoIterable collections = dispatchDatastore.getDatabase().listCollectionNames(); - for (String name : collections) { - dispatchDatastore.getDatabase().getCollection(name).dropIndexes(); - } - // Add back indexes + if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { + dispatchMongoClient = new MongoClient(new MongoClientURI(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl)); + dispatchDatastore = morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection); + + // Ensure indexes for dispatch server + try { dispatchDatastore.ensureIndexes(); + } catch (MongoCommandException e) { + Grasscutter.getLogger().info("Mongo index error: ", e); + // Duplicate index error + if (e.getCode() == 85) { + // Drop all indexes and re add them + MongoIterable collections = dispatchDatastore.getDatabase().listCollectionNames(); + for (String name : collections) { + dispatchDatastore.getDatabase().getCollection(name).dropIndexes(); + } + // Add back indexes + dispatchDatastore.ensureIndexes(); + } } } } From 9867ce7369c52870bf2fb797f71778e3da163058 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Thu, 21 Apr 2022 22:55:32 +0800 Subject: [PATCH 64/91] Add list command Signed-off-by: Jaida Wu --- .../command/commands/ListCommand.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/emu/grasscutter/command/commands/ListCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/ListCommand.java b/src/main/java/emu/grasscutter/command/commands/ListCommand.java new file mode 100644 index 000000000..6afca4a6d --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/ListCommand.java @@ -0,0 +1,33 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; +import java.util.Map; + +@Command(label = "list", description = "List online players") +public class ListCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + Map playersMap = Grasscutter.getGameServer().getPlayers(); + + CommandHandler.sendMessage(sender, String.format("There are %s player(s) online:", playersMap.size())); + + if (playersMap.size() != 0) { + StringBuilder playerSet = new StringBuilder(); + + for (Map.Entry entry : playersMap.entrySet()) { + playerSet.append(entry.getValue().getNickname()); + playerSet.append(", "); + } + + String players = playerSet.toString(); + + CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2)); + } + } +} From 2c509237a5d8f11f9382dbb153d9efd5df5b4555 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 23:00:36 +0800 Subject: [PATCH 65/91] Removed class left over from testing --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 37ed6c5ac..1978e6b08 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -515,9 +515,4 @@ public final class DispatchServer { this.Base64 = b64; } } - - public static class UserConnnectionData { - public InetAddress dispatchConnectedAddress; - public String connectedRegion; - } } From beb8675190000de00e2ae40b6e524b4ae5ad4b5c Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Thu, 21 Apr 2022 23:04:02 +0800 Subject: [PATCH 66/91] Should probably remove this as well... --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 1978e6b08..f77c264eb 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -50,7 +50,6 @@ public final class DispatchServer { public String regionListBase64; public HashMap regions; - public HashMap usersConnected; public static String query_region_list = ""; public static String query_cur_region = ""; From d6377fb2158f33aa20a2271feae1283ea2eed6cc Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Thu, 21 Apr 2022 22:45:02 +0700 Subject: [PATCH 67/91] Update README.md Removed !sethp, added !setstats, !pos, !weather --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19f2a1e3e..24269f6b9 100644 --- a/README.md +++ b/README.md @@ -60,10 +60,14 @@ There is a dummy user named "Server" in every player's friends list that you can `!resetconst` - Resets the constellation level on your current active character, will need to relog after using the command to see any changes. -`!sethp [hp]` +`!setstats [stats] [amount]` - Changes the current character's specified stat. `!clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including yellow rarity ones** from your inventory +`!pos` - Gets your current coordinate. + +`!weather [weather id]` - Changes the current weather. + *More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).* ### Bonus From 23226ad4649f1d1f30473737c41d388fcb55e18a Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Thu, 21 Apr 2022 22:06:25 +0800 Subject: [PATCH 68/91] Refact code Signed-off-by: Jaida Wu --- src/main/java/emu/grasscutter/Config.java | 12 +- .../server/dispatch/DispatchServer.java | 283 ++++++++---------- .../dispatch/json/ComboTokenReqJson.java | 2 +- .../dispatch/json/ComboTokenResJson.java | 2 +- .../server/dispatch/json/LoginResultJson.java | 4 +- 5 files changed, 141 insertions(+), 162 deletions(-) diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index 65ada3969..f51e5cd59 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -12,10 +12,10 @@ public final class Config { public String GameServerIp = "127.0.0.1"; public String GameServerPublicIp = ""; public int GameServerPort = 22102; - + public String DatabaseUrl = "mongodb://localhost:27017"; public String DatabaseCollection = "grasscutter"; - + public String RESOURCE_FOLDER = "./resources/"; public String DATA_FOLDER = "./data/"; public String PACKETS_FOLDER = "./packets/"; @@ -25,21 +25,21 @@ public final class Config { public GameRates Game = new GameRates(); public ServerOptions ServerOptions = new ServerOptions(); - + public GameRates getGameRates() { return Game; } - + public ServerOptions getServerOptions() { return ServerOptions; } - + public static class GameRates { public float ADVENTURE_EXP_RATE = 1.0f; public float MORA_RATE = 1.0f; public float DOMAIN_DROP_RATE = 1.0f; } - + public static class ServerOptions { public int InventoryLimitWeapon = 2000; public int InventoryLimitRelic = 2000; diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index e59f9d251..b4d4993d8 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -1,10 +1,27 @@ package emu.grasscutter.server.dispatch; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.protobuf.ByteString; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; +import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp; +import emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.QueryRegionListHttpRsp; +import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo; +import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo; +import emu.grasscutter.server.dispatch.json.*; +import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import java.io.*; import java.net.InetSocketAddress; import java.net.URI; import java.net.URLDecoder; @@ -14,58 +31,27 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.protobuf.ByteString; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpsConfigurator; -import com.sun.net.httpserver.HttpsServer; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.Account; -import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp; -import emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.QueryRegionListHttpRsp; -import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo; -import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo; -import emu.grasscutter.server.dispatch.json.ComboTokenReqJson; -import emu.grasscutter.server.dispatch.json.ComboTokenResJson; -import emu.grasscutter.server.dispatch.json.LoginAccountRequestJson; -import emu.grasscutter.server.dispatch.json.LoginResultJson; -import emu.grasscutter.server.dispatch.json.LoginTokenRequestJson; -import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData; -import emu.grasscutter.utils.FileUtils; -import emu.grasscutter.utils.Utils; - -import com.sun.net.httpserver.HttpServer; - public final class DispatchServer { - private final InetSocketAddress address; - private final Gson gson; - private QueryCurrRegionHttpRsp currRegion; - - public String regionListBase64; - public String regionCurrentBase64; - public static String query_region_list = ""; public static String query_cur_region = ""; - + private final InetSocketAddress address; + private final Gson gson; + public String regionListBase64; + public String regionCurrentBase64; + private QueryCurrRegionHttpRsp currRegion; + public DispatchServer() { this.address = new InetSocketAddress(Grasscutter.getConfig().DispatchServerIp, Grasscutter.getConfig().DispatchServerPort); this.gson = new GsonBuilder().create(); - + this.loadQueries(); this.initRegion(); } - + public InetSocketAddress getAddress() { return address; } - + public Gson getGsonFactory() { return gson; } @@ -73,17 +59,17 @@ public final class DispatchServer { public QueryCurrRegionHttpRsp getCurrRegion() { return currRegion; } - + public void loadQueries() { File file; - + file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_region_list.txt"); if (file.exists()) { query_region_list = new String(FileUtils.read(file)); } else { Grasscutter.getLogger().warn("query_region_list not found! Using default region list."); } - + file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt"); if (file.exists()) { query_cur_region = new String(FileUtils.read(file)); @@ -96,38 +82,38 @@ public final class DispatchServer { try { byte[] decoded = Base64.getDecoder().decode(query_region_list); QueryRegionListHttpRsp rl = QueryRegionListHttpRsp.parseFrom(decoded); - + byte[] decoded2 = Base64.getDecoder().decode(query_cur_region); QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2); - + RegionSimpleInfo server = RegionSimpleInfo.newBuilder() .setName("os_usa") .setTitle(Grasscutter.getConfig().GameServerName) .setType("DEV_PUBLIC") .setDispatchUrl("https://" + (Grasscutter.getConfig().DispatchServerPublicIp.isEmpty() ? Grasscutter.getConfig().DispatchServerIp : Grasscutter.getConfig().DispatchServerPublicIp) + ":" + getAddress().getPort() + "/query_cur_region") .build(); - + RegionSimpleInfo serverTest2 = RegionSimpleInfo.newBuilder() .setName("os_euro") .setTitle("Grasscutter") .setType("DEV_PUBLIC") .setDispatchUrl("https://" + (Grasscutter.getConfig().DispatchServerPublicIp.isEmpty() ? Grasscutter.getConfig().DispatchServerIp : Grasscutter.getConfig().DispatchServerPublicIp) + ":" + getAddress().getPort() + "/query_cur_region") .build(); - + QueryRegionListHttpRsp regionList = QueryRegionListHttpRsp.newBuilder() - .addServers(server) - .addServers(serverTest2) - .setClientSecretKey(rl.getClientSecretKey()) - .setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted()) - .setEnableLoginPc(true) - .build(); - + .addServers(server) + .addServers(serverTest2) + .setClientSecretKey(rl.getClientSecretKey()) + .setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted()) + .setEnableLoginPc(true) + .build(); + RegionInfo currentRegion = regionQuery.getRegionInfo().toBuilder() .setIp((Grasscutter.getConfig().GameServerPublicIp.isEmpty() ? Grasscutter.getConfig().GameServerIp : Grasscutter.getConfig().GameServerPublicIp)) .setPort(Grasscutter.getConfig().GameServerPort) .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) .build(); - + QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(currentRegion).build(); this.regionListBase64 = Base64.getEncoder().encodeToString(regionList.toByteString().toByteArray()); @@ -140,7 +126,7 @@ public final class DispatchServer { public void start() throws Exception { HttpServer server; - if(Grasscutter.getConfig().UseSSL) { + if (Grasscutter.getConfig().UseSSL) { HttpsServer httpsServer; httpsServer = HttpsServer.create(getAddress(), 0); SSLContext sslContext = SSLContext.getInstance("TLS"); @@ -150,9 +136,9 @@ public final class DispatchServer { ks.load(fis, keystorePassword); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, keystorePassword); - + sslContext.init(kmf.getKeyManagers(), null, null); - + httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); server = httpsServer; } catch (Exception e) { @@ -162,7 +148,7 @@ public final class DispatchServer { } else { server = HttpServer.create(getAddress(), 0); } - + server.createContext("/", t -> { //Create a response form the request query parameters String response = "Hello"; @@ -174,7 +160,7 @@ public final class DispatchServer { os.write(response.getBytes()); os.close(); }); - + // Dispatch server.createContext("/query_region_list", t -> { // Log @@ -213,33 +199,38 @@ public final class DispatchServer { try { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, LoginAccountRequestJson.class); - } catch (Exception e) { - + } catch (Exception ignored) { + } // Create response json if (requestData == null) { return; } LoginResultJson responseData = new LoginResultJson(); - + // Login Account account = DatabaseHelper.getAccountByName(requestData.account); - + // Check if account exists, else create a new one. if (account == null) { // Account doesnt exist, so we can either auto create it if the config value is set if (Grasscutter.getConfig().ServerOptions.AutomaticallyCreateAccounts) { // This account has been created AUTOMATICALLY. There will be no permissions added. account = DatabaseHelper.createAccountWithId(requestData.account, 0); - - responseData.message = "OK"; - responseData.data.account.uid = account.getId(); - responseData.data.account.token = account.generateSessionKey(); - responseData.data.account.email = account.getEmail(); + + if (account != null) { + responseData.message = "OK"; + responseData.data.account.uid = account.getId(); + responseData.data.account.token = account.generateSessionKey(); + responseData.data.account.email = account.getEmail(); + } else { + responseData.retcode = -201; + responseData.message = "Username not found, create failed."; + } } else { responseData.retcode = -201; responseData.message = "Username not found."; - } + } } else { // Account was found, log the player in responseData.message = "OK"; @@ -247,16 +238,8 @@ public final class DispatchServer { responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); } - - // Create a response - String response = getGsonFactory().toJson(responseData); - // Set the response header status and length - t.getResponseHeaders().put("Content-Type", Collections.singletonList("application/json")); - t.sendResponseHeaders(200, response.getBytes().length); - // Write the response string - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); + + responseJson(t, responseData); }); // Login via token server.createContext("/hk4e_global/mdk/shield/api/verify", t -> { @@ -265,8 +248,8 @@ public final class DispatchServer { try { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, LoginTokenRequestJson.class); - } catch (Exception e) { - + } catch (Exception ignored) { + } // Create response json if (requestData == null) { @@ -276,7 +259,7 @@ public final class DispatchServer { // Login Account account = DatabaseHelper.getAccountById(requestData.uid); - + // Test if (account == null || !account.getSessionKey().equals(requestData.token)) { responseData.retcode = -111; @@ -287,16 +270,8 @@ public final class DispatchServer { responseData.data.account.token = requestData.token; responseData.data.account.email = account.getEmail(); } - - // Create a response - String response = getGsonFactory().toJson(responseData); - // Set the response header status and length - t.getResponseHeaders().put("Content-Type", Collections.singletonList("application/json")); - t.sendResponseHeaders(200, response.getBytes().length); - // Write the response string - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); + + responseJson(t, responseData); }); // Exchange for combo token server.createContext("/hk4e_global/combo/granter/login/v2/login", t -> { @@ -305,8 +280,8 @@ public final class DispatchServer { try { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, ComboTokenReqJson.class); - } catch (Exception e) { - + } catch (Exception ignored) { + } // Create response json if (requestData == null || requestData.data == null) { @@ -317,7 +292,7 @@ public final class DispatchServer { // Login Account account = DatabaseHelper.getAccountById(loginData.uid); - + // Test if (account == null || !account.getSessionKey().equals(loginData.token)) { responseData.retcode = -201; @@ -328,29 +303,21 @@ public final class DispatchServer { responseData.data.combo_id = "157795300"; responseData.data.combo_token = account.generateLoginToken(); } - - // Create a response - String response = getGsonFactory().toJson(responseData); - // Set the response header status and length - t.getResponseHeaders().put("Content-Type", Collections.singletonList("application/json")); - t.sendResponseHeaders(200, response.getBytes().length); - // Write the response string - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); + + responseJson(t, responseData); }); // Agreement and Protocol server.createContext( // hk4e-sdk-os.hoyoverse.com - "/hk4e_global/mdk/agreement/api/getAgreementInfos", + "/hk4e_global/mdk/agreement/api/getAgreementInfos", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}") ); server.createContext( // hk4e-sdk-os.hoyoverse.com - "/hk4e_global/combo/granter/api/compareProtocolVersion", + "/hk4e_global/combo/granter/api/compareProtocolVersion", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}") ); // Game data server.createContext( // hk4e-api-os.hoyoverse.com - "/common/hk4e_global/announcement/api/getAlertPic", + "/common/hk4e_global/announcement/api/getAlertPic", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}") ); server.createContext( // hk4e-api-os.hoyoverse.com @@ -358,62 +325,62 @@ public final class DispatchServer { new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}") ); server.createContext( // hk4e-api-os.hoyoverse.com - "/common/hk4e_global/announcement/api/getAnnList", + "/common/hk4e_global/announcement/api/getAnnList", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0,\"type_list\":[],\"alert\":false,\"alert_id\":0,\"timezone\":0,\"t\":\"" + System.currentTimeMillis() + "\"}}") ); server.createContext( // hk4e-api-os-static.hoyoverse.com - "/common/hk4e_global/announcement/api/getAnnContent", + "/common/hk4e_global/announcement/api/getAnnContent", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0}}") ); server.createContext( // hk4e-sdk-os.hoyoverse.com - "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", + "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}") ); // Captcha server.createContext( // api-account-os.hoyoverse.com - "/account/risky/api/check", + "/account/risky/api/check", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"c8820f246a5241ab9973f71df3ddd791\",\"action\":\"\",\"geetest\":{\"challenge\":\"\",\"gt\":\"\",\"new_captcha\":0,\"success\":1}}}") ); // Config server.createContext( // sdk-os-static.hoyoverse.com - "/combo/box/api/config/sdk/combo", + "/combo/box/api/config/sdk/combo", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}") ); server.createContext( // hk4e-sdk-os-static.hoyoverse.com - "/hk4e_global/combo/granter/api/getConfig", + "/hk4e_global/combo/granter/api/getConfig", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\\u0026sdk_screen_transparent=true\\u0026game_biz=hk4e_global\\u0026auth_appid=announcement\\u0026game=hk4e#/\",\"push_alias_type\":2,\"disable_ysdk_guard\":false,\"enable_announce_pic_popup\":true}}") ); server.createContext( // hk4e-sdk-os-static.hoyoverse.com - "/hk4e_global/mdk/shield/api/loadConfig", + "/hk4e_global/mdk/shield/api/loadConfig", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":6,\"game_key\":\"hk4e_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"原神海外\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{\"tw\":\"\",\"fb\":\"\"},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}}}}") ); // Test api? server.createContext( // abtest-api-data-sg.hoyoverse.com - "/data_abtest_api/config/experiment/list", + "/data_abtest_api/config/experiment/list", new DispatchHttpJsonHandler("{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}") ); // Log Server server.createContext( // log-upload-os.mihoyo.com - "/log/sdk/upload", + "/log/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}") ); server.createContext( // log-upload-os.mihoyo.com - "/sdk/upload", + "/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}") ); server.createContext( // /perf/config/verify?device_id=xxx&platform=x&name=xxx - "/perf/config/verify", + "/perf/config/verify", new DispatchHttpJsonHandler("{\"code\":0}") ); - + // Logging servers server.createContext( // overseauspider.yuanshen.com - "/log", + "/log", new DispatchHttpJsonHandler("{\"code\":0}") ); - + server.createContext( // log-upload-os.mihoyo.com - "/crash/dataUpload", + "/crash/dataUpload", new DispatchHttpJsonHandler("{\"code\":0}") ); server.createContext("/gacha", t -> { @@ -431,31 +398,43 @@ public final class DispatchServer { server.start(); Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort()); } - + + private void responseJson(HttpExchange t, Object data) throws IOException { + // Create a response + String response = getGsonFactory().toJson(data); + // Set the response header status and length + t.getResponseHeaders().put("Content-Type", Collections.singletonList("application/json")); + t.sendResponseHeaders(200, response.getBytes().length); + // Write the response string + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + private Map parseQueryString(String qs) { - Map result = new HashMap<>(); - if (qs == null) - return result; + Map result = new HashMap<>(); + if (qs == null) + return result; - int last = 0, next, l = qs.length(); - while (last < l) { - next = qs.indexOf('&', last); - if (next == -1) - next = l; + int last = 0, next, l = qs.length(); + while (last < l) { + next = qs.indexOf('&', last); + if (next == -1) + next = l; - if (next > last) { - int eqPos = qs.indexOf('=', last); - try { - if (eqPos < 0 || eqPos > next) - result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), ""); - else - result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java - } - } - last = next + 1; - } - return result; + if (next > last) { + int eqPos = qs.indexOf('=', last); + try { + if (eqPos < 0 || eqPos > next) + result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), ""); + else + result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java + } + } + last = next + 1; + } + return result; } } diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenReqJson.java b/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenReqJson.java index dac26cfa4..b3497f8d4 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenReqJson.java +++ b/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenReqJson.java @@ -7,7 +7,7 @@ public class ComboTokenReqJson { public String device; public String sign; - public class LoginTokenData { + public static class LoginTokenData { public String uid; public String token; public boolean guest; diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenResJson.java b/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenResJson.java index 731d50853..7c49d1278 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenResJson.java +++ b/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenResJson.java @@ -5,7 +5,7 @@ public class ComboTokenResJson { public int retcode; public LoginData data = new LoginData(); - public class LoginData { + public static class LoginData { public int account_type = 1; public boolean heartbeat; public String combo_id; diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java b/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java index 5988752d8..88e142d4f 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java +++ b/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java @@ -5,7 +5,7 @@ public class LoginResultJson { public int retcode; public VerifyData data = new VerifyData(); - public class VerifyData { + public static class VerifyData { public VerifyAccountData account = new VerifyAccountData(); public boolean device_grant_required = false; public String realname_operation = "NONE"; @@ -13,7 +13,7 @@ public class LoginResultJson { public boolean safe_mobile_required = false; } - public class VerifyAccountData { + public static class VerifyAccountData { public String uid; public String name = ""; public String email; From 12674f6cec64259d930c5eb8680f46c8e00e5335 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Thu, 21 Apr 2022 22:21:28 +0800 Subject: [PATCH 69/91] Fallback to HTTP if no SSL cert Signed-off-by: Jaida Wu --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index b4d4993d8..392609a07 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -142,8 +142,9 @@ public final class DispatchServer { httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); server = httpsServer; } catch (Exception e) { - Grasscutter.getLogger().error("No SSL cert found!"); - return; + Grasscutter.getLogger().error("No SSL cert found! Falling back to HTTP server."); + Grasscutter.getConfig().UseSSL = false; + server = HttpServer.create(getAddress(), 0); } } else { server = HttpServer.create(getAddress(), 0); From 1143886b091a3ee88708da9f2176315b1f1239d3 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Thu, 21 Apr 2022 22:22:40 +0800 Subject: [PATCH 70/91] Add some log to dispatch server Signed-off-by: Jaida Wu --- .../server/dispatch/DispatchServer.java | 100 +++++++++--------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 392609a07..0e2283652 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -150,48 +150,26 @@ public final class DispatchServer { server = HttpServer.create(getAddress(), 0); } - server.createContext("/", t -> { - //Create a response form the request query parameters - String response = "Hello"; - //Set the response header status and length - t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8")); - t.sendResponseHeaders(200, response.getBytes().length); - //Write the response string - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - }); + server.createContext("/", t -> responseHTML(t, "Hello")); // Dispatch server.createContext("/query_region_list", t -> { // Log - Grasscutter.getLogger().info("Client request: query_region_list"); - // Create a response form the request query parameters - String response = regionListBase64; - // Set the response header status and length - t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8")); - t.sendResponseHeaders(200, response.getBytes().length); - // Write the response string - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); + Grasscutter.getLogger().info(String.format("Client %s request: query_region_list", t.getRemoteAddress())); + + responseHTML(t, regionListBase64); }); server.createContext("/query_cur_region", t -> { // Log - Grasscutter.getLogger().info("Client request: query_cur_region"); + Grasscutter.getLogger().info(String.format("Client %s request: query_cur_region", t.getRemoteAddress())); // Create a response form the request query parameters URI uri = t.getRequestURI(); String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="; if (uri.getQuery() != null && uri.getQuery().length() > 0) { response = regionCurrentBase64; } - // Set the response header status and length - t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8")); - t.sendResponseHeaders(200, response.getBytes().length); - // Write the response string - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); + + responseHTML(t, response); }); // Login via account server.createContext("/hk4e_global/mdk/shield/api/login", t -> { @@ -201,7 +179,6 @@ public final class DispatchServer { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, LoginAccountRequestJson.class); } catch (Exception ignored) { - } // Create response json if (requestData == null) { @@ -209,6 +186,8 @@ public final class DispatchServer { } LoginResultJson responseData = new LoginResultJson(); + Grasscutter.getLogger().info(String.format("Client %s is trying to log in", t.getRemoteAddress())); + // Login Account account = DatabaseHelper.getAccountByName(requestData.account); @@ -224,13 +203,19 @@ public final class DispatchServer { responseData.data.account.uid = account.getId(); responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); + + Grasscutter.getLogger().info(String.format("Client %s failed to log in: Account %s created", t.getRemoteAddress(), responseData.data.account.uid)); } else { responseData.retcode = -201; responseData.message = "Username not found, create failed."; + + Grasscutter.getLogger().info(String.format("Client %s failed to log in: Account create failed", t.getRemoteAddress())); } } else { responseData.retcode = -201; responseData.message = "Username not found."; + + Grasscutter.getLogger().info(String.format("Client %s failed to log in: Account no found", t.getRemoteAddress())); } } else { // Account was found, log the player in @@ -238,10 +223,13 @@ public final class DispatchServer { responseData.data.account.uid = account.getId(); responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); + + Grasscutter.getLogger().info(String.format("Client %s logged in as %s", t.getRemoteAddress(), responseData.data.account.uid)); } - responseJson(t, responseData); + responseJSON(t, responseData); }); + // Login via token server.createContext("/hk4e_global/mdk/shield/api/verify", t -> { // Get post data @@ -250,13 +238,13 @@ public final class DispatchServer { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, LoginTokenRequestJson.class); } catch (Exception ignored) { - } // Create response json if (requestData == null) { return; } LoginResultJson responseData = new LoginResultJson(); + Grasscutter.getLogger().info(String.format("Client %s is trying to log in via token", t.getRemoteAddress())); // Login Account account = DatabaseHelper.getAccountById(requestData.uid); @@ -265,14 +253,18 @@ public final class DispatchServer { if (account == null || !account.getSessionKey().equals(requestData.token)) { responseData.retcode = -111; responseData.message = "Game account cache information error"; + + Grasscutter.getLogger().info(String.format("Client %s failed to log in via token", t.getRemoteAddress())); } else { responseData.message = "OK"; responseData.data.account.uid = requestData.uid; responseData.data.account.token = requestData.token; responseData.data.account.email = account.getEmail(); + + Grasscutter.getLogger().info(String.format("Client %s logged in via token as %s", t.getRemoteAddress(), responseData.data.account.uid)); } - responseJson(t, responseData); + responseJSON(t, responseData); }); // Exchange for combo token server.createContext("/hk4e_global/combo/granter/login/v2/login", t -> { @@ -282,7 +274,6 @@ public final class DispatchServer { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, ComboTokenReqJson.class); } catch (Exception ignored) { - } // Create response json if (requestData == null || requestData.data == null) { @@ -298,14 +289,18 @@ public final class DispatchServer { if (account == null || !account.getSessionKey().equals(loginData.token)) { responseData.retcode = -201; responseData.message = "Wrong session key."; + + Grasscutter.getLogger().info(String.format("Client %s failed to exchange combo token", t.getRemoteAddress())); } else { responseData.message = "OK"; responseData.data.open_id = loginData.uid; responseData.data.combo_id = "157795300"; responseData.data.combo_token = account.generateLoginToken(); + + Grasscutter.getLogger().info(String.format("Client %s succeed to exchange combo token", t.getRemoteAddress())); } - responseJson(t, responseData); + responseJSON(t, responseData); }); // Agreement and Protocol server.createContext( // hk4e-sdk-os.hoyoverse.com @@ -384,23 +379,13 @@ public final class DispatchServer { "/crash/dataUpload", new DispatchHttpJsonHandler("{\"code\":0}") ); - server.createContext("/gacha", t -> { - //Create a response form the request query parameters - String response = "Gacha"; - //Set the response header status and length - t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8")); - t.sendResponseHeaders(200, response.getBytes().length); - //Write the response string - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - }); + server.createContext("/gacha", t -> responseHTML(t, "Gacha")); // Start server server.start(); Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort()); } - private void responseJson(HttpExchange t, Object data) throws IOException { + private void responseJSON(HttpExchange t, Object data) throws IOException { // Create a response String response = getGsonFactory().toJson(data); // Set the response header status and length @@ -412,24 +397,37 @@ public final class DispatchServer { os.close(); } + private void responseHTML(HttpExchange t, String response) throws IOException { + // Set the response header status and length + t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8")); + t.sendResponseHeaders(200, response.getBytes().length); + //Write the response string + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + private Map parseQueryString(String qs) { Map result = new HashMap<>(); - if (qs == null) + if (qs == null) { return result; + } int last = 0, next, l = qs.length(); while (last < l) { next = qs.indexOf('&', last); - if (next == -1) + if (next == -1) { next = l; + } if (next > last) { int eqPos = qs.indexOf('=', last); try { - if (eqPos < 0 || eqPos > next) + if (eqPos < 0 || eqPos > next) { result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), ""); - else + } else { result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8")); + } } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java } From d11413c0170ef6b2d4fa258ca7e7714f560320d1 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Thu, 21 Apr 2022 22:26:28 +0800 Subject: [PATCH 71/91] Add dispatch server log prefix Signed-off-by: Jaida Wu --- .../server/dispatch/DispatchServer.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 0e2283652..5b46f9def 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -67,14 +67,14 @@ public final class DispatchServer { if (file.exists()) { query_region_list = new String(FileUtils.read(file)); } else { - Grasscutter.getLogger().warn("query_region_list not found! Using default region list."); + Grasscutter.getLogger().warn("[Dispatch] query_region_list not found! Using default region list."); } file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt"); if (file.exists()) { query_cur_region = new String(FileUtils.read(file)); } else { - Grasscutter.getLogger().warn("query_cur_region not found! Using default current region."); + Grasscutter.getLogger().warn("[Dispatch] query_cur_region not found! Using default current region."); } } @@ -120,7 +120,7 @@ public final class DispatchServer { this.regionCurrentBase64 = Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()); this.currRegion = parsedRegionQuery; } catch (Exception e) { - Grasscutter.getLogger().error("Error while initializing region info!", e); + Grasscutter.getLogger().error("[Dispatch] Error while initializing region info!", e); } } @@ -142,7 +142,7 @@ public final class DispatchServer { httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); server = httpsServer; } catch (Exception e) { - Grasscutter.getLogger().error("No SSL cert found! Falling back to HTTP server."); + Grasscutter.getLogger().warn("[Dispatch] No SSL cert found! Falling back to HTTP server."); Grasscutter.getConfig().UseSSL = false; server = HttpServer.create(getAddress(), 0); } @@ -155,13 +155,13 @@ public final class DispatchServer { // Dispatch server.createContext("/query_region_list", t -> { // Log - Grasscutter.getLogger().info(String.format("Client %s request: query_region_list", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s request: query_region_list", t.getRemoteAddress())); responseHTML(t, regionListBase64); }); server.createContext("/query_cur_region", t -> { // Log - Grasscutter.getLogger().info(String.format("Client %s request: query_cur_region", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s request: query_cur_region", t.getRemoteAddress())); // Create a response form the request query parameters URI uri = t.getRequestURI(); String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="; @@ -186,7 +186,7 @@ public final class DispatchServer { } LoginResultJson responseData = new LoginResultJson(); - Grasscutter.getLogger().info(String.format("Client %s is trying to log in", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s is trying to log in", t.getRemoteAddress())); // Login Account account = DatabaseHelper.getAccountByName(requestData.account); @@ -204,18 +204,18 @@ public final class DispatchServer { responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); - Grasscutter.getLogger().info(String.format("Client %s failed to log in: Account %s created", t.getRemoteAddress(), responseData.data.account.uid)); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account %s created", t.getRemoteAddress(), responseData.data.account.uid)); } else { responseData.retcode = -201; responseData.message = "Username not found, create failed."; - Grasscutter.getLogger().info(String.format("Client %s failed to log in: Account create failed", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account create failed", t.getRemoteAddress())); } } else { responseData.retcode = -201; responseData.message = "Username not found."; - Grasscutter.getLogger().info(String.format("Client %s failed to log in: Account no found", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account no found", t.getRemoteAddress())); } } else { // Account was found, log the player in @@ -224,7 +224,7 @@ public final class DispatchServer { responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); - Grasscutter.getLogger().info(String.format("Client %s logged in as %s", t.getRemoteAddress(), responseData.data.account.uid)); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", t.getRemoteAddress(), responseData.data.account.uid)); } responseJSON(t, responseData); @@ -244,7 +244,7 @@ public final class DispatchServer { return; } LoginResultJson responseData = new LoginResultJson(); - Grasscutter.getLogger().info(String.format("Client %s is trying to log in via token", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s is trying to log in via token", t.getRemoteAddress())); // Login Account account = DatabaseHelper.getAccountById(requestData.uid); @@ -254,14 +254,14 @@ public final class DispatchServer { responseData.retcode = -111; responseData.message = "Game account cache information error"; - Grasscutter.getLogger().info(String.format("Client %s failed to log in via token", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in via token", t.getRemoteAddress())); } else { responseData.message = "OK"; responseData.data.account.uid = requestData.uid; responseData.data.account.token = requestData.token; responseData.data.account.email = account.getEmail(); - Grasscutter.getLogger().info(String.format("Client %s logged in via token as %s", t.getRemoteAddress(), responseData.data.account.uid)); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s", t.getRemoteAddress(), responseData.data.account.uid)); } responseJSON(t, responseData); @@ -290,14 +290,14 @@ public final class DispatchServer { responseData.retcode = -201; responseData.message = "Wrong session key."; - Grasscutter.getLogger().info(String.format("Client %s failed to exchange combo token", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to exchange combo token", t.getRemoteAddress())); } else { responseData.message = "OK"; responseData.data.open_id = loginData.uid; responseData.data.combo_id = "157795300"; responseData.data.combo_token = account.generateLoginToken(); - Grasscutter.getLogger().info(String.format("Client %s succeed to exchange combo token", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s succeed to exchange combo token", t.getRemoteAddress())); } responseJSON(t, responseData); @@ -382,7 +382,7 @@ public final class DispatchServer { server.createContext("/gacha", t -> responseHTML(t, "Gacha")); // Start server server.start(); - Grasscutter.getLogger().info("Dispatch server started on port " + getAddress().getPort()); + Grasscutter.getLogger().info("[Dispatch] Dispatch server started on port " + getAddress().getPort()); } private void responseJSON(HttpExchange t, Object data) throws IOException { From 54856147ef4750ef76277663f3c84555809ff6d9 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Thu, 21 Apr 2022 22:27:26 +0800 Subject: [PATCH 72/91] Listens to all addresses by default Signed-off-by: Jaida Wu --- src/main/java/emu/grasscutter/Config.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index f51e5cd59..0b011e576 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -1,16 +1,16 @@ package emu.grasscutter; public final class Config { - public String DispatchServerIp = "127.0.0.1"; - public String DispatchServerPublicIp = ""; + public String DispatchServerIp = "0.0.0.0"; + public String DispatchServerPublicIp = "127.0.0.1"; public int DispatchServerPort = 443; public String DispatchServerKeystorePath = "./keystore.p12"; public String DispatchServerKeystorePassword = ""; public Boolean UseSSL = true; public String GameServerName = "Test"; - public String GameServerIp = "127.0.0.1"; - public String GameServerPublicIp = ""; + public String GameServerIp = "0.0.0.0"; + public String GameServerPublicIp = "127.0.0.1"; public int GameServerPort = 22102; public String DatabaseUrl = "mongodb://localhost:27017"; From 23caf994c79c27d6c6e4f03af311ec52eb48cf40 Mon Sep 17 00:00:00 2001 From: Benjamin Elsdon Date: Fri, 22 Apr 2022 01:15:05 +0800 Subject: [PATCH 73/91] Forgot a dispatch prefix on error while merging --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index a058bed6b..ad8354867 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -155,7 +155,7 @@ public final class DispatchServer { this.regionListBase64 = Base64.getEncoder().encodeToString(regionList.toByteString().toByteArray()); } catch (Exception e) { - Grasscutter.getLogger().error("Error while initializing region info!", e); + Grasscutter.getLogger().error("[Dispatch] Error while initializing region info!", e); } } From 0480f10a1af4a13e9e981a636231a516a9e231dd Mon Sep 17 00:00:00 2001 From: yarik0chka Date: Thu, 21 Apr 2022 22:21:24 +0500 Subject: [PATCH 74/91] Add a changing weather id --- README.md | 2 +- .../grasscutter/command/commands/WeatherCommand.java | 12 +++++++----- src/main/java/emu/grasscutter/game/GenshinScene.java | 11 ++++++++++- .../packet/send/PacketSceneAreaWeatherNotify.java | 2 +- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 24269f6b9..1aef752c0 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ There is a dummy user named "Server" in every player's friends list that you can `!pos` - Gets your current coordinate. -`!weather [weather id]` - Changes the current weather. +`!weather [weather id] [climate id]` - Changes the current weather. *More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).* diff --git a/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java b/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java index 919432ed1..bb9ae4a42 100644 --- a/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java @@ -8,7 +8,7 @@ import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify; import java.util.List; -@Command(label = "weather", usage = "weather ", +@Command(label = "weather", usage = "weather [climateId]", description = "Changes the weather.", aliases = {"w"}, permission = "player.weather") public final class WeatherCommand implements CommandHandler { @@ -20,20 +20,22 @@ public final class WeatherCommand implements CommandHandler { } if (args.size() < 1) { - CommandHandler.sendMessage(sender, "Usage: weather "); + CommandHandler.sendMessage(sender, "Usage: weather [climateId]"); return; } try { int weatherId = Integer.parseInt(args.get(0)); + int climateId = args.size() > 1 ? Integer.parseInt(args.get(1)) : 1; - ClimateType climate = ClimateType.getTypeByValue(weatherId); + ClimateType climate = ClimateType.getTypeByValue(climateId); + sender.getScene().setWeather(weatherId); sender.getScene().setClimate(climate); sender.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(sender)); - CommandHandler.sendMessage(sender, "Changed weather to " + weatherId); + CommandHandler.sendMessage(sender, "Changed weather to " + weatherId + " with climate " + climateId); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, "Invalid weather ID."); + CommandHandler.sendMessage(sender, "Invalid ID."); } } } diff --git a/src/main/java/emu/grasscutter/game/GenshinScene.java b/src/main/java/emu/grasscutter/game/GenshinScene.java index 668902d87..ea462fcdb 100644 --- a/src/main/java/emu/grasscutter/game/GenshinScene.java +++ b/src/main/java/emu/grasscutter/game/GenshinScene.java @@ -34,7 +34,8 @@ public class GenshinScene { private int time; private ClimateType climate; - + private int weather; + public GenshinScene(World world, SceneData sceneData) { this.world = world; this.sceneData = sceneData; @@ -89,10 +90,18 @@ public class GenshinScene { return climate; } + public int getWeather() { + return weather; + } + public void setClimate(ClimateType climate) { this.climate = climate; } + public void setWeather(int weather) { + this.weather = weather; + } + public boolean isInScene(GenshinEntity entity) { return this.entities.containsKey(entity.getId()); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneAreaWeatherNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneAreaWeatherNotify.java index de26dd064..69c40dba0 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneAreaWeatherNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneAreaWeatherNotify.java @@ -12,7 +12,7 @@ public class PacketSceneAreaWeatherNotify extends GenshinPacket { super(PacketOpcodes.SceneAreaWeatherNotify); SceneAreaWeatherNotify proto = SceneAreaWeatherNotify.newBuilder() - .setWeatherAreaId(1) + .setWeatherAreaId(player.getScene().getWeather()) .setClimateType(player.getScene().getClimate().getValue()) .build(); From 452e89b4db728ed745fa290ead03278854fd3f82 Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Fri, 22 Apr 2022 00:54:06 +0700 Subject: [PATCH 75/91] Fix typo "When you create a new account via auto register or console server then during scene select a character and write a name on it press it it will get stuck and make the server disconnected. Some can get through and some can't passed it, so currently I'm still on the stable version . even how many times have I deleted the datebase and tried it on some linux and directly on windows too :/ btw https://github.com/Grasscutters/Grasscutter/blob/0e7976f906648ab63a2c049b5c4bdc146702cb8f/src/main/java/emu/grasscutter/data/ResourceLoader.java#L131 in scene folder there is a typo name "BinOutPut" it should be "BinOutput" so scene file can't be found." - [Yuki#4985](https://discord.com/channels/965284035985305680/965284036333424722/966756877100789760) Thanks [Yuki#4985](https://discord.com/channels/965284035985305680/965284036333424722/966756877100789760) --- src/main/java/emu/grasscutter/data/ResourceLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index 8a29f5a72..bb95d70e9 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -128,7 +128,7 @@ public class ResourceLoader { private static void loadScenePoints() { Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)"); - File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutPut/Scene/Point"); + File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Scene/Point"); if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) { Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!"); From 069cb1c6d3e3f1883bb0b2fcb7060cc96180a153 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Fri, 22 Apr 2022 02:23:02 +0800 Subject: [PATCH 76/91] Teleport Small Fix --- src/main/java/emu/grasscutter/game/World.java | 9 ++++++++- .../server/packet/send/PacketPlayerEnterSceneNotify.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/World.java b/src/main/java/emu/grasscutter/game/World.java index f88d3bee4..ffffb1e53 100644 --- a/src/main/java/emu/grasscutter/game/World.java +++ b/src/main/java/emu/grasscutter/game/World.java @@ -212,7 +212,10 @@ public class World implements Iterable { return false; } + Integer oldSceneId = null; + if (player.getScene() != null) { + oldSceneId = player.getScene().getId(); player.getScene().removePlayer(player); } @@ -221,7 +224,11 @@ public class World implements Iterable { player.getPos().set(pos); // Teleport packet - player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterSelf, EnterReason.TransPoint, sceneId, pos)); + if (oldSceneId.equals(sceneId)) { + player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterGoto, EnterReason.TransPoint, sceneId, pos)); + } else { + player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.EnterJump, EnterReason.TransPoint, sceneId, pos)); + } return true; } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java index e2db99153..d66575273 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java @@ -52,7 +52,7 @@ public class PacketPlayerEnterSceneNotify extends GenshinPacket { .setSceneId(newScene) .setPos(newPos.toProto()) .setSceneBeginTime(System.currentTimeMillis()) - .setType(EnterType.EnterSelf) + .setType(type) .setTargetUid(target.getUid()) .setEnterSceneToken(player.getEnterSceneToken()) .setWorldLevel(target.getWorld().getWorldLevel()) From abe05a9b5d7897aec427a6c38048190d8f8d15f7 Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Fri, 22 Apr 2022 02:28:52 +0700 Subject: [PATCH 77/91] Added /revive command Now it's more convenient to revive character without food :> --- .../grasscutter/command/commands/Revive.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/emu/grasscutter/command/commands/Revive.java diff --git a/src/main/java/emu/grasscutter/command/commands/Revive.java b/src/main/java/emu/grasscutter/command/commands/Revive.java new file mode 100644 index 000000000..3f830f3e2 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Revive.java @@ -0,0 +1,33 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.TeamManager; +//import emu.grasscutter.game.avatar.GenshinAvatar; +//import emu.grasscutter.game.TeamInfo; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; + +import java.util.List; + +@Command(label = "revive", aliases = {"rev"}, + usage = "revive|rev", description = "Revive character(s) that died)") +public class Revive implements CommandHandler { + //private Object teamId; + + @Override + public void execute(GenshinPlayer player, List args) { + for (EntityAvatar entity2 : TeamManager.getActiveTeam()) { + entity2.setFightProperty( + FightProperty.FIGHT_PROP_CUR_HP, + entity2.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .4f + ); + player.sendPacket(new PacketAvatarFightPropUpdateNotify(entity2.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + player.sendPacket(new PacketAvatarLifeStateChangeNotify(entity2.getAvatar())); + } + player.dropMessage("Character revived."); + } +} From e10bad5b170892b05b1c737505c9786aa117336e Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Thu, 21 Apr 2022 14:02:17 -0700 Subject: [PATCH 78/91] Fix account dupe key error after selecting starting character --- src/main/java/emu/grasscutter/game/GenshinPlayer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 1bc989183..95ae5176d 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -170,7 +170,6 @@ public class GenshinPlayer { public void setUid(int id) { this.id = id; - this.getProfile().syncWithCharacter(this); } public long getNextGenshinGuid() { @@ -353,7 +352,6 @@ public class GenshinPlayer { public PlayerProfile getProfile() { if (this.playerProfile == null) { this.playerProfile = new PlayerProfile(this); - this.save(); } return playerProfile; } From 58535dd1cec3648bff89c23752927888334f1759 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Thu, 21 Apr 2022 14:44:55 -0700 Subject: [PATCH 79/91] Save config everytime you load so the new options show up --- src/main/java/emu/grasscutter/Grasscutter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 0e34c120d..51d0835cd 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -86,8 +86,10 @@ public final class Grasscutter { public static void loadConfig() { try (FileReader file = new FileReader(configFile)) { config = gson.fromJson(file, Config.class); + saveConfig(); } catch (Exception e) { - Grasscutter.config = new Config(); saveConfig(); + Grasscutter.config = new Config(); + saveConfig(); } } From 04791b68be5f8956b4d676547f8456739e376423 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Thu, 21 Apr 2022 14:47:04 -0700 Subject: [PATCH 80/91] Refactor Revive Command + fix syntax error No idea how I missed the syntax error while reviewing --- .../{Revive.java => ReviveCommand.java} | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) rename src/main/java/emu/grasscutter/command/commands/{Revive.java => ReviveCommand.java} (88%) diff --git a/src/main/java/emu/grasscutter/command/commands/Revive.java b/src/main/java/emu/grasscutter/command/commands/ReviveCommand.java similarity index 88% rename from src/main/java/emu/grasscutter/command/commands/Revive.java rename to src/main/java/emu/grasscutter/command/commands/ReviveCommand.java index 3f830f3e2..554af4a50 100644 --- a/src/main/java/emu/grasscutter/command/commands/Revive.java +++ b/src/main/java/emu/grasscutter/command/commands/ReviveCommand.java @@ -1,33 +1,33 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.TeamManager; -//import emu.grasscutter.game.avatar.GenshinAvatar; -//import emu.grasscutter.game.TeamInfo; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; - -import java.util.List; - -@Command(label = "revive", aliases = {"rev"}, - usage = "revive|rev", description = "Revive character(s) that died)") -public class Revive implements CommandHandler { - //private Object teamId; - - @Override - public void execute(GenshinPlayer player, List args) { - for (EntityAvatar entity2 : TeamManager.getActiveTeam()) { - entity2.setFightProperty( - FightProperty.FIGHT_PROP_CUR_HP, - entity2.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .4f - ); - player.sendPacket(new PacketAvatarFightPropUpdateNotify(entity2.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); - player.sendPacket(new PacketAvatarLifeStateChangeNotify(entity2.getAvatar())); - } - player.dropMessage("Character revived."); - } -} +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.TeamManager; +//import emu.grasscutter.game.avatar.GenshinAvatar; +//import emu.grasscutter.game.TeamInfo; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; + +import java.util.List; + +@Command(label = "revive", aliases = {"rev"}, + usage = "revive|rev", description = "Revive character(s) that died)") +public class ReviveCommand implements CommandHandler { + //private Object teamId; + + @Override + public void execute(GenshinPlayer player, List args) { + for (EntityAvatar entity2 : player.getTeamManager().getActiveTeam()) { + entity2.setFightProperty( + FightProperty.FIGHT_PROP_CUR_HP, + entity2.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .4f + ); + player.sendPacket(new PacketAvatarFightPropUpdateNotify(entity2.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + player.sendPacket(new PacketAvatarLifeStateChangeNotify(entity2.getAvatar())); + } + player.dropMessage("Character revived."); + } +} From eedab79253154942e70168277325540694817250 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Fri, 22 Apr 2022 07:37:25 +0800 Subject: [PATCH 81/91] fetter try --- .../emu/grasscutter/data/GenshinData.java | 15 +++++++ .../emu/grasscutter/data/def/AvatarData.java | 9 ++++ .../emu/grasscutter/data/def/FetterData.java | 24 +++++++++++ .../game/avatar/GenshinAvatar.java | 34 ++++++++++++++- .../grasscutter/game/props/FetterState.java | 42 +++++++++++++++++++ 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/def/FetterData.java create mode 100644 src/main/java/emu/grasscutter/game/props/FetterState.java diff --git a/src/main/java/emu/grasscutter/data/GenshinData.java b/src/main/java/emu/grasscutter/data/GenshinData.java index 7dc5a5f7c..1996bf074 100644 --- a/src/main/java/emu/grasscutter/data/GenshinData.java +++ b/src/main/java/emu/grasscutter/data/GenshinData.java @@ -1,7 +1,9 @@ package emu.grasscutter.data; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import emu.grasscutter.Grasscutter; @@ -54,6 +56,7 @@ public class GenshinData { private static final Int2ObjectMap avatarCostumeDataItemIdMap = new Int2ObjectLinkedOpenHashMap<>(); private static final Int2ObjectMap sceneDataMap = new Int2ObjectLinkedOpenHashMap<>(); + private static final Int2ObjectMap fetterDataMap = new Int2ObjectOpenHashMap<>(); public static Int2ObjectMap getMapByResourceDef(Class resourceDefinition) { Int2ObjectMap map = null; @@ -221,4 +224,16 @@ public class GenshinData { public static Int2ObjectMap getSceneDataMap() { return sceneDataMap; } + + public static Map> getFetterDataEntries() { + // Can I do this? + Map> fetters = new HashMap<>(); + fetterDataMap.forEach((k, v) -> { + if (!fetters.containsKey(v.getAvatarId())) { + fetters.put(v.getAvatarId(), new ArrayList<>()); + } + fetters.get(v.getAvatarId()).add(k); + }); + return fetters; + } } diff --git a/src/main/java/emu/grasscutter/data/def/AvatarData.java b/src/main/java/emu/grasscutter/data/def/AvatarData.java index dcc503994..8097e04a7 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarData.java +++ b/src/main/java/emu/grasscutter/data/def/AvatarData.java @@ -55,6 +55,8 @@ public class AvatarData extends GenshinResource { private float[] defenseGrowthCurve; private AvatarSkillDepotData skillDepot; private IntList abilities; + + private List fetters; @Override public int getId(){ @@ -193,9 +195,16 @@ public class AvatarData extends GenshinResource { return abilities; } + public List getFetters() { + return fetters; + } + @Override public void onLoad() { this.skillDepot = GenshinData.getAvatarSkillDepotDataMap().get(this.SkillDepotId); + + // Get fetters from GenshinData + this.fetters = GenshinData.getFetterDataEntries().get(this.Id); int size = GenshinData.getAvatarCurveDataMap().size(); this.hpGrowthCurve = new float[size]; diff --git a/src/main/java/emu/grasscutter/data/def/FetterData.java b/src/main/java/emu/grasscutter/data/def/FetterData.java new file mode 100644 index 000000000..d17c940d1 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/FetterData.java @@ -0,0 +1,24 @@ +package emu.grasscutter.data.def; + +import emu.grasscutter.data.GenshinResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.ResourceType.LoadPriority; + +@ResourceType(name = {"FetterInfoExcelConfigData.json", "FettersExcelConfigData.json", "FetterStoryExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST) +public class FetterData extends GenshinResource { + private int AvatarId; + private int FetterId; + + @Override + public int getId() { + return FetterId; + } + + public int getAvatarId() { + return AvatarId; + } + + @Override + public void onLoad() { + } +} diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index 9723c23d7..e5dccc42d 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -1,7 +1,9 @@ package emu.grasscutter.game.avatar; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -39,9 +41,11 @@ import emu.grasscutter.game.inventory.EquipType; import emu.grasscutter.game.inventory.GenshinItem; import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.EntityIdType; +import emu.grasscutter.game.props.FetterState; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo; +import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData; import emu.grasscutter.net.proto.AvatarInfoOuterClass.AvatarInfo; import emu.grasscutter.server.packet.send.PacketAbilityChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify; @@ -73,6 +77,8 @@ public class GenshinAvatar { @Transient private final Int2FloatOpenHashMap fightProp; @Transient private Set extraAbilityEmbryos; + private List fetters; + private Map skillLevelMap; // Talent levels private Map proudSkillBonusMap; // Talent bonus levels (from const) private int skillDepotId; @@ -89,7 +95,8 @@ public class GenshinAvatar { this.equips = new Int2ObjectOpenHashMap<>(); this.fightProp = new Int2FloatOpenHashMap(); this.extraAbilityEmbryos = new HashSet<>(); - this.proudSkillBonusMap = new HashMap<>(); // TODO Move to genshin avatar + this.proudSkillBonusMap = new HashMap<>(); + this.fetters = new ArrayList<>(); // TODO Move to genshin avatar } // On creation @@ -266,6 +273,14 @@ public class GenshinAvatar { return extraAbilityEmbryos; } + public void setFetterList(List fetterList) { + this.fetterList = fetterList; + } + + public List getFetterList() { + return fetterList; + } + public float getCurrentHp() { return currentHp; } @@ -385,6 +400,9 @@ public class GenshinAvatar { // Extra ability embryos Set prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos(); this.extraAbilityEmbryos = new HashSet<>(); + + // Fetters + this.setFetterList(data.getFetters()); // Get hp percent, set to 100% if none float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); @@ -683,6 +701,18 @@ public class GenshinAvatar { } public AvatarInfo toProto() { + AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() + .setExpLevel(10) + .setExpNumber(6325); // Highest Level + + for (int i = 0; i < this.getFetterList().size(); i++) { + avatarFetter.addFetterList( + FetterData.newBuilder() + .setFetterId(this.getFetterList().get(i)) + .setFetterState(FetterState.FINISH.getValue()) + ); + } + AvatarInfo.Builder avatarInfo = AvatarInfo.newBuilder() .setAvatarId(this.getAvatarId()) .setGuid(this.getGuid()) @@ -696,7 +726,7 @@ public class GenshinAvatar { .putAllProudSkillExtraLevel(getProudSkillBonusMap()) .setAvatarType(1) .setBornTime(this.getBornTime()) - .setFetterInfo(AvatarFetterInfo.newBuilder().setExpLevel(1)) + .setFetterInfo(avatarFetter) .setWearingFlycloakId(this.getFlyCloak()) .setCostumeId(this.getCostume()); diff --git a/src/main/java/emu/grasscutter/game/props/FetterState.java b/src/main/java/emu/grasscutter/game/props/FetterState.java new file mode 100644 index 000000000..346060e19 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/props/FetterState.java @@ -0,0 +1,42 @@ +package emu.grasscutter.game.props; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public enum FetterState { + NONE(0), + NOT_OPEN(1), + OPEN(1), + FINISH(3); + + private final int value; + private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + private static final Map stringMap = new HashMap<>(); + + static { + Stream.of(values()).forEach(e -> { + map.put(e.getValue(), e); + stringMap.put(e.name(), e); + }); + } + + private FetterState(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static FetterState getTypeByValue(int value) { + return map.getOrDefault(value, NONE); + } + + public static FetterState getTypeByName(String name) { + return stringMap.getOrDefault(name, NONE); + } +} From 044c2a3086ca290218a46d3a71b7f2ff39929cf4 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Fri, 22 Apr 2022 07:42:17 +0800 Subject: [PATCH 82/91] typo --- src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index e5dccc42d..d3b4d7147 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -77,7 +77,7 @@ public class GenshinAvatar { @Transient private final Int2FloatOpenHashMap fightProp; @Transient private Set extraAbilityEmbryos; - private List fetters; + private List fetters; private Map skillLevelMap; // Talent levels private Map proudSkillBonusMap; // Talent bonus levels (from const) From 2350f0ebe2f55d6f44a6591f4809008d2354391b Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Fri, 22 Apr 2022 07:49:35 +0800 Subject: [PATCH 83/91] ee, forget to change variables when copying from local --- src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index d3b4d7147..0560ba4e5 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -274,11 +274,11 @@ public class GenshinAvatar { } public void setFetterList(List fetterList) { - this.fetterList = fetterList; + this.fetters = fetterList; } public List getFetterList() { - return fetterList; + return fetters; } public float getCurrentHp() { From 28db79876bbbd762093a67a17136c3ca4d365a3e Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Fri, 22 Apr 2022 08:08:47 +0800 Subject: [PATCH 84/91] add fullfp command --- .../command/commands/FullHpCommand.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/emu/grasscutter/command/commands/FullHpCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/FullHpCommand.java b/src/main/java/emu/grasscutter/command/commands/FullHpCommand.java new file mode 100644 index 000000000..f27533a8d --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/FullHpCommand.java @@ -0,0 +1,36 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; + +import java.util.List; + +@Command(label = "fullhp", usage = "fullhp|fhp", + description = "Recover all characters in your current team to full health.", aliases = {"fhp"}, permission = "player.fullhp") +public class FullHpCommand implements CommandHandler { + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + sender.getTeamManager().getActiveTeam().forEach(entity -> { + boolean isAlive = entity.isAlive(); + entity.setFightProperty( + FightProperty.FIGHT_PROP_CUR_HP, + entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) + ); + entity.getWorld().broadcastPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + if (!isAlive) { + entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); + } + }); + CommandHandler.sendMessage(sender, "All hp recover to full."); + } +} From ff778914cbed61fd16f657c85998e0725a890f76 Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Fri, 22 Apr 2022 08:26:48 +0800 Subject: [PATCH 85/91] remove revive command --- .../command/commands/ReviveCommand.java | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 src/main/java/emu/grasscutter/command/commands/ReviveCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/ReviveCommand.java b/src/main/java/emu/grasscutter/command/commands/ReviveCommand.java deleted file mode 100644 index 554af4a50..000000000 --- a/src/main/java/emu/grasscutter/command/commands/ReviveCommand.java +++ /dev/null @@ -1,33 +0,0 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.TeamManager; -//import emu.grasscutter.game.avatar.GenshinAvatar; -//import emu.grasscutter.game.TeamInfo; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; - -import java.util.List; - -@Command(label = "revive", aliases = {"rev"}, - usage = "revive|rev", description = "Revive character(s) that died)") -public class ReviveCommand implements CommandHandler { - //private Object teamId; - - @Override - public void execute(GenshinPlayer player, List args) { - for (EntityAvatar entity2 : player.getTeamManager().getActiveTeam()) { - entity2.setFightProperty( - FightProperty.FIGHT_PROP_CUR_HP, - entity2.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .4f - ); - player.sendPacket(new PacketAvatarFightPropUpdateNotify(entity2.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); - player.sendPacket(new PacketAvatarLifeStateChangeNotify(entity2.getAvatar())); - } - player.dropMessage("Character revived."); - } -} From 376c668dd9e66af525bb9dfcbf49bedcacb2e662 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Fri, 22 Apr 2022 08:30:02 +0800 Subject: [PATCH 86/91] no more NullPointerException pls --- .../emu/grasscutter/game/avatar/GenshinAvatar.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index 0560ba4e5..6906d3896 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -705,12 +705,14 @@ public class GenshinAvatar { .setExpLevel(10) .setExpNumber(6325); // Highest Level - for (int i = 0; i < this.getFetterList().size(); i++) { - avatarFetter.addFetterList( - FetterData.newBuilder() - .setFetterId(this.getFetterList().get(i)) - .setFetterState(FetterState.FINISH.getValue()) - ); + if (this.getFetterList() != null) { + for (int i = 0; i < this.getFetterList().size(); i++) { + avatarFetter.addFetterList( + FetterData.newBuilder() + .setFetterId(this.getFetterList().get(i)) + .setFetterState(FetterState.FINISH.getValue()) + ); + } } AvatarInfo.Builder avatarInfo = AvatarInfo.newBuilder() From b31dab04eb9ee7dfa16d0170dcea88cd3c55440a Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Fri, 22 Apr 2022 08:45:46 +0800 Subject: [PATCH 87/91] change fhp to heal --- .../commands/{FullHpCommand.java => HealCommand.java} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/main/java/emu/grasscutter/command/commands/{FullHpCommand.java => HealCommand.java} (81%) diff --git a/src/main/java/emu/grasscutter/command/commands/FullHpCommand.java b/src/main/java/emu/grasscutter/command/commands/HealCommand.java similarity index 81% rename from src/main/java/emu/grasscutter/command/commands/FullHpCommand.java rename to src/main/java/emu/grasscutter/command/commands/HealCommand.java index f27533a8d..511e52812 100644 --- a/src/main/java/emu/grasscutter/command/commands/FullHpCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/HealCommand.java @@ -11,9 +11,9 @@ import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import java.util.List; -@Command(label = "fullhp", usage = "fullhp|fhp", - description = "Recover all characters in your current team to full health.", aliases = {"fhp"}, permission = "player.fullhp") -public class FullHpCommand implements CommandHandler { +@Command(label = "heal", usage = "heal|h", + description = "Heal all characters in your current team.", aliases = {"h"}, permission = "player.heal") +public class HealCommand implements CommandHandler { @Override public void execute(GenshinPlayer sender, List args) { if (sender == null) { @@ -31,6 +31,6 @@ public class FullHpCommand implements CommandHandler { entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); } }); - CommandHandler.sendMessage(sender, "All hp recover to full."); + CommandHandler.sendMessage(sender, "All characters are healed."); } } From 123fc263ba75b877a453fd610e61d5e7ed1c0bd6 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Fri, 22 Apr 2022 10:39:25 +0800 Subject: [PATCH 88/91] cache fetters --- .../emu/grasscutter/data/GenshinData.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/emu/grasscutter/data/GenshinData.java b/src/main/java/emu/grasscutter/data/GenshinData.java index 1996bf074..7e14b1b46 100644 --- a/src/main/java/emu/grasscutter/data/GenshinData.java +++ b/src/main/java/emu/grasscutter/data/GenshinData.java @@ -57,6 +57,9 @@ public class GenshinData { private static final Int2ObjectMap sceneDataMap = new Int2ObjectLinkedOpenHashMap<>(); private static final Int2ObjectMap fetterDataMap = new Int2ObjectOpenHashMap<>(); + + // Cache + private static Map> fetters = new HashMap<>(); public static Int2ObjectMap getMapByResourceDef(Class resourceDefinition) { Int2ObjectMap map = null; @@ -226,14 +229,15 @@ public class GenshinData { } public static Map> getFetterDataEntries() { - // Can I do this? - Map> fetters = new HashMap<>(); - fetterDataMap.forEach((k, v) -> { - if (!fetters.containsKey(v.getAvatarId())) { - fetters.put(v.getAvatarId(), new ArrayList<>()); - } - fetters.get(v.getAvatarId()).add(k); - }); + if (fetters.isEmpty()) { + fetterDataMap.forEach((k, v) -> { + if (!fetters.containsKey(v.getAvatarId())) { + fetters.put(v.getAvatarId(), new ArrayList<>()); + } + fetters.get(v.getAvatarId()).add(k); + }); + } + return fetters; } } From 7bc944544da81e4ca28c3b64cc0572922f6ede4a Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Fri, 22 Apr 2022 11:32:49 +0800 Subject: [PATCH 89/91] Make proxy daemon run imperceptibly Signed-off-by: Jaida Wu --- run.cmd => start.cmd | 84 ++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 35 deletions(-) rename run.cmd => start.cmd (61%) diff --git a/run.cmd b/start.cmd similarity index 61% rename from run.cmd rename to start.cmd index 36a20d811..e3425d0e4 100644 --- a/run.cmd +++ b/start.cmd @@ -4,67 +4,90 @@ @if "%DEBUG%" == "" echo off pushd %~dp0 +set CUR_PATH=%~dp0 title Grasscutter -call :LOG [INFO] Grasscutter +call :LOG [INFO] Welcome to Grasscutter +call :LOG [INFO] To proper exit this console, use [Ctrl + C] and enter N not Y. +call :LOG [INFO] call :LOG [INFO] Initializing... -@rem This will not work if your java or mitmproxy is in a different location, plugin as necessary -@rem this just saves you from changing your PATH -set JAVA_PATH=C:\Program Files\Java\jdk1.8.0_202\ -set MITMPROXY_PATH=%~dp0 -set PROXY_SCRIPT=proxy @rem TODO: MongoDB integration -set SERVER_PATH=%~dp0 +set JAVA_PATH=DO_NOT_CHECK_PATH +set MITMDUMP_PATH=DO_NOT_CHECK_PATH + +set SERVER_JAR_PATH=%CUR_PATH% + +set SERVER_JAR_NAME=grasscutter.jar +set PROXY_SCRIPT_NAME=proxy + +if not "%JAVA_PATH%" == "DO_NOT_CHECK_PATH" ( + if not exist "%JAVA_PATH%java.exe" ( + call :LOG [ERROR] Java not found. + goto :EXIT + ) +) else set JAVA_PATH= +if not exist "%SERVER_PATH%grasscutter.jar" ( + call :LOG [ERROR] Server jar not found. + goto :EXIT +) @rem mitmproxy not found, server only -if not exist "%MITMPROXY_PATH%mitmdump.exe" ( - call :LOG [WARN] mitmproxy not found, server only mode. - goto :SERVER -) +if not "%MITMDUMP_PATH%" == "DO_NOT_CHECK_PATH" ( + if not exist "%MITMDUMP_PATH%mitmdump.exe" ( + call :LOG [WARN] mitmdump not found, server only mode. + goto :SERVER + ) +) else set MITMDUMP_PATH= @rem proxy script not found, server only -if not exist "%PROXY_SCRIPT%.py" ( - if not exist "%PROXY_SCRIPT%.pyc" ( +if not exist "%PROXY_SCRIPT_NAME%.py" ( + if not exist "%PROXY_SCRIPT_NAME%.pyc" ( call :LOG [WARN] Missing proxy script or compiled proxy script, server only mode. goto :SERVER - ) else set PROXY_SCRIPT=%PROXY_SCRIPT%.pyc -) else set PROXY_SCRIPT=%PROXY_SCRIPT%.py + ) else set PROXY_SCRIPT_NAME=%PROXY_SCRIPT_NAME%.pyc +) else set PROXY_SCRIPT_NAME=%PROXY_SCRIPT_NAME%.py :PROXY @rem UAC Administrator privileges >nul 2>&1 reg query "HKU\S-1-5-19" || ( call :LOG [WARN] Currently running with non Administrator privileges, raising... echo set UAC = CreateObject^("Shell.Application"^) > "%temp%\UAC.vbs" - echo UAC.ShellExecute "%~f0", "%1", "", "runas", 1 >> "%temp%\UAC.vbs" + echo UAC.ShellExecute "%~f0","%1","","runas",1 >> "%temp%\UAC.vbs" "%temp%\UAC.vbs" del /f /q "%temp%\UAC.vbs" >nul 2>nul exit /b ) +call :LOG [INFO] Starting proxy daemon... + set PROXY=true + @rem Store original proxy settings for /f "tokens=2*" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable 2^>nul') do set "ORIG_PROXY_ENABLE=%%b" for /f "tokens=2*" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer 2^>nul') do set "ORIG_PROXY_SERVER=%%b" -call :LOG [INFO] Starting proxy daemon... @rem TODO: External proxy when ORIG_PROXY_ENABLE == 0x1 -start /min "" "%MITMPROXY_PATH%mitmdump.exe" -s %PROXY_SCRIPT% --ssl-insecure +echo set ws = createobject("wscript.shell") > "%temp%\proxy.vbs" +echo ws.currentdirectory = "%MITMDUMP_PATH%" >> "%temp%\proxy.vbs" +echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k",0 >> "%temp%\proxy.vbs" +"%temp%\proxy.vbs" +del /f /q "%temp%\proxy.vbs" >nul 2>nul -@rem CA certificate for possible HTTPS scheme +@rem CA certificate for HTTPS scheme call :LOG [INFO] Waiting for CA certificate generation... set CA_CERT_FILE="%USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer" -set /A TIMEOUT_COUNT=0 +set /a TIMEOUT_COUNT=0 :CERT_CA_CHECK if not exist %CA_CERT_FILE% ( - timeout /T 1 >nul 2>nul - set /A TIMEOUT_COUNT+=1 + timeout /t 1 >nul 2>nul + set /a TIMEOUT_COUNT+=1 goto CERT_CA_CHECK ) :EXTRA_TIMEOUT if %TIMEOUT_COUNT% LEQ 2 ( - timeout /T 1 >nul 2>nul - set /A TIMEOUT_COUNT+=1 + timeout /t 1 >nul 2>nul + set /a TIMEOUT_COUNT+=1 goto EXTRA_TIMEOUT ) call :LOG [INFO] Adding CA certificate to store... @@ -75,21 +98,13 @@ reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v Pr reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "127.0.0.1:8080" /f >nul 2>nul :SERVER -if not exist "%JAVA_PATH%bin\java.exe" ( - call :LOG [ERROR] Java not found. - goto :EXIT -) -if not exist "%SERVER_PATH%grasscutter.jar" ( - call :LOG [ERROR] Server jar not found. - goto :EXIT -) call :LOG [INFO] Starting server... -"%JAVA_PATH%bin\java.exe" -jar "%SERVER_PATH%grasscutter.jar" +"%JAVA_PATH%java.exe" -jar "%SERVER_PATH%grasscutter.jar" call :LOG [INFO] Server stopped :EXIT if "%PROXY%" == "" ( - call :LOG [INFO] Proxy not started, no need to clean up. + call :LOG [INFO] Proxy daemon not started, no need to clean up. ) else ( call :LOG [INFO] Restoring network settings... @@ -100,7 +115,6 @@ if "%PROXY%" == "" ( taskkill /t /f /im mitmdump.exe >nul 2>nul call :LOG [INFO] Removing CA certificate... - for /F "tokens=2" %%s in ('certutil -dump %CA_CERT_FILE% ^| findstr ^"^sha1^"') do ( set SERIAL=%%s ) From b336d89962922b5b718cdd86cb9de882569043ac Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Fri, 22 Apr 2022 11:33:54 +0800 Subject: [PATCH 90/91] Integrate MongoDB into C2R Signed-off-by: Jaida Wu --- start.cmd | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/start.cmd b/start.cmd index e3425d0e4..bf154bfd5 100644 --- a/start.cmd +++ b/start.cmd @@ -11,11 +11,12 @@ call :LOG [INFO] To proper exit this console, use [Ctrl + C] and enter N not Y. call :LOG [INFO] call :LOG [INFO] Initializing... -@rem TODO: MongoDB integration set JAVA_PATH=DO_NOT_CHECK_PATH set MITMDUMP_PATH=DO_NOT_CHECK_PATH +set MONGODB_PATH=DO_NOT_CHECK_PATH set SERVER_JAR_PATH=%CUR_PATH% +set DATABASE_STORAGE_PATH=%CUR_PATH%resources\Database set SERVER_JAR_NAME=grasscutter.jar set PROXY_SCRIPT_NAME=proxy @@ -98,11 +99,35 @@ reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v Pr reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "127.0.0.1:8080" /f >nul 2>nul :SERVER +if not "%MONGODB_PATH%" == "DO_NOT_CHECK_PATH" ( + if not exist "%MONGODB_PATH%mongod.exe" ( + call :LOG [WARN] MongoDB daemon not found, server only mode. + goto :GAME + ) +) else set MONGODB_PATH= +call :LOG [INFO] Starting MongoDB daemon... +set DATABASE=true + +mkdir "%DATABASE_STORAGE_PATH%" >nul 2>nul + +echo set ws = createobject("wscript.shell") > "%temp%\db.vbs" +echo ws.currentdirectory = "%MONGODB_PATH%" >> "%temp%\db.vbs" +echo ws.run "cmd /c mongod.exe --dbpath "^&chr(34)^&"%DATABASE_STORAGE_PATH%"^&chr(34)^&"",0 >> "%temp%\db.vbs" +"%temp%\db.vbs" +del /f /q "%temp%\db.vbs" >nul 2>nul + +:GAME call :LOG [INFO] Starting server... "%JAVA_PATH%java.exe" -jar "%SERVER_PATH%grasscutter.jar" call :LOG [INFO] Server stopped :EXIT +if "%DATABASE%" == "" ( + call :LOG [INFO] MongoDB daemon not started, no need to clean up. +) else ( + call :LOG [INFO] Shutting down MongoDB daemon... + taskkill /t /f /im mongod.exe >nul 2>nul +) if "%PROXY%" == "" ( call :LOG [INFO] Proxy daemon not started, no need to clean up. ) else ( From 725851f5a14264d37c3ab7570f82c92cad506e0d Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Fri, 22 Apr 2022 11:34:22 +0800 Subject: [PATCH 91/91] Allow configuration overrides Signed-off-by: Jaida Wu --- start.cmd | 6 ++++++ start_config.cmd | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 start_config.cmd diff --git a/start.cmd b/start.cmd index bf154bfd5..8c89f359e 100644 --- a/start.cmd +++ b/start.cmd @@ -11,6 +11,8 @@ call :LOG [INFO] To proper exit this console, use [Ctrl + C] and enter N not Y. call :LOG [INFO] call :LOG [INFO] Initializing... +set CONFIG=start_config + set JAVA_PATH=DO_NOT_CHECK_PATH set MITMDUMP_PATH=DO_NOT_CHECK_PATH set MONGODB_PATH=DO_NOT_CHECK_PATH @@ -21,6 +23,10 @@ set DATABASE_STORAGE_PATH=%CUR_PATH%resources\Database set SERVER_JAR_NAME=grasscutter.jar set PROXY_SCRIPT_NAME=proxy +if exist "%CUR_PATH%%CONFIG%.cmd" ( + call "%CUR_PATH%%CONFIG%.cmd" >nul 2>nul +) + if not "%JAVA_PATH%" == "DO_NOT_CHECK_PATH" ( if not exist "%JAVA_PATH%java.exe" ( call :LOG [ERROR] Java not found. diff --git a/start_config.cmd b/start_config.cmd new file mode 100644 index 000000000..575e4d8df --- /dev/null +++ b/start_config.cmd @@ -0,0 +1,25 @@ +@rem +@rem Copyright (C) 2002-2022 MlgmXyysd All Rights Reserved. +@rem + +@echo off +pushd %~dp0 +set CUR_PATH=%~dp0 + +@rem This will not work if your java or mitmproxy is in a different location, plugin as necessary +@rem this just saves you from changing your PATH + +@rem Executable Path +@rem Note: Fill DO_NOT_CHECK_PATH if you need to run it from PATH +@rem without detecting whether the executable file exists +set JAVA_PATH=C:\Program Files\Java\jdk1.8.0_202\bin\ +set MITMDUMP_PATH=%CUR_PATH% +set MONGODB_PATH=%CUR_PATH% + +@rem Utility Path +set SERVER_JAR_PATH=%CUR_PATH% +set DATABASE_STORAGE_PATH=%CUR_PATH%resources\Database + +@rem Utility Name +set SERVER_JAR_NAME=grasscutter.jar +set PROXY_SCRIPT_NAME=proxy \ No newline at end of file