├── .gitattributes ├── .gitignore ├── README.md ├── cpp ├── PhoenixAPI.sln ├── PhoenixAPI │ ├── Api.cpp │ ├── Api.h │ ├── PhoenixAPI.vcxproj │ ├── PhoenixAPI.vcxproj.filters │ ├── Port_finder.cpp │ ├── Port_finder.h │ ├── Safe_queue.cpp │ ├── Safe_queue.h │ └── main.cpp └── examples │ ├── InstantCombat │ ├── Bot.cpp │ ├── Bot.h │ ├── InstantCombat.vcxproj │ ├── InstantCombat.vcxproj.filters │ ├── Logger.cpp │ ├── Logger.h │ ├── Module.h │ ├── PhoenixApi │ │ ├── Api.cpp │ │ ├── Api.h │ │ ├── Port_finder.cpp │ │ ├── Port_finder.h │ │ ├── Safe_queue.cpp │ │ └── Safe_queue.h │ ├── README.md │ ├── Scene.cpp │ ├── Scene.h │ ├── Split_string.cpp │ ├── Split_string.h │ └── main.cpp │ └── Ts26_Bot │ ├── Bot.cpp │ ├── Bot.h │ ├── Module.h │ ├── PhoenixApi │ ├── Api.cpp │ ├── Api.h │ ├── Port_finder.cpp │ ├── Port_finder.h │ ├── Safe_queue.cpp │ └── Safe_queue.h │ ├── Player.cpp │ ├── Player.h │ ├── Scene.cpp │ ├── Scene.h │ ├── Split_string.cpp │ ├── Split_string.h │ ├── Ts26_Bot.vcxproj │ ├── Ts26_Bot.vcxproj.filters │ └── main.cpp └── python ├── example.py └── phoenixapi ├── __init__.py ├── finder.py └── phoenix.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_h.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *_wpftmp.csproj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush personal settings 296 | .cr/personal 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | 333 | # Local History for Visual Studio 334 | .localhistory/ 335 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PhoenixAPI 2 | 3 | ## General explanation 4 | 5 | This API example has been made for C++ but you can use the API with any programming language that supports TCP sockets and JSON. This example basicaly provides a function to find all the running bot ports and a class that holds the socket that will be connected to a specific port and receive all the data from the bot. 6 | 7 | **Important:** Messages are terminated with `'\1'` character or `0x01` byte. This examples already manages that so you should not worrie about it, however if you plan to use the API with other language you need to handle that correctly. 8 | 9 | The messages received from the bot and sent to the bot are in JSON format and they match the following syntax: 10 | 11 | ```js 12 | { 13 | "type": 0, // The type of the message 14 | 15 | // [...] 16 | // Further parameters of the message, depends on the type 17 | } 18 | ``` 19 | 20 | The type is just a numerical value and it must be a valid type. In the C++ example it is represented by an enum for easier access: 21 | 22 | ```cpp 23 | enum class Type 24 | { 25 | packet_send, // 0 26 | packet_recv, // 1 27 | attack, // 2 28 | player_skill, // 3 29 | player_walk, // 4 30 | pet_skill, // 5 31 | partner_skill, // 6 32 | pets_walk, // 7 33 | pick_up, // 8 34 | collect, // 9 35 | start_bot, // 10 36 | stop_bot, // 11 37 | continue_bot, // 12 38 | load_settings, // 13 39 | start_minigame_bot, // 14 40 | stop_minigame_bot, // 15 41 | query_player_info, // 16 42 | query_inventory, // 17 43 | query_skills_info, // 18 44 | query_map_entities // 19 45 | } 46 | ``` 47 | 48 | ## Messages sent from the BOT to the CLIENT 49 | 50 | The bot will constantly send you messages of type 0 and 1 which are send and recv packets. These 2 types of messages also have the field `packet` with the full packet. 51 | 52 | Examples: 53 | 54 | * Send packet 55 | 56 | ```json 57 | { 58 | "type": 0, 59 | "packet": "u_s 0 3 2432" 60 | } 61 | ``` 62 | 63 | ___ 64 | 65 | * Recv packet 66 | 67 | ```json 68 | { 69 | "type": 1, 70 | "packet": "mv 3 2389 70 11 5" 71 | } 72 | ``` 73 | 74 | You can also query the player infomation, inventory, skills and all map entities information using the messages with type 16, 17, 18 and 19. 75 | 76 | Examples: 77 | 78 | * Player information 79 | 80 | ```json 81 | { 82 | "type": 16, 83 | "player_info": { 84 | "name": "Hatz", 85 | "id": 123123, 86 | "x": 5, 87 | "y": 8, 88 | "dest_x": 25, 89 | "dest_y": 69, 90 | "level": 99, 91 | "champion_level": 80, 92 | "hp_percent": 100, 93 | "mp_percent": 100, 94 | "map_id": 1, 95 | "is_resting": false 96 | } 97 | } 98 | ``` 99 | 100 | * Inventory 101 | 102 | ```json 103 | { 104 | "type": 17, 105 | "inventory": { 106 | "equip": [ 107 | { 108 | "quantity": 1, 109 | "name": "Beast King's Crossbow", 110 | "position": 0, 111 | "vnum": 4465 112 | }, 113 | { 114 | "quantity": 1, 115 | "name": "Great Leader's Crossbow", 116 | "position": 1, 117 | "vnum": 4007 118 | } 119 | ], 120 | "main": [ 121 | { 122 | "quantity": 70, 123 | "name": "Gillion Stone", 124 | "position": 0, 125 | "vnum": 1013 126 | }, 127 | { 128 | "quantity": 311, 129 | "name": "Seed of Power", 130 | "position": 1, 131 | "vnum": 1012 132 | } 133 | ], 134 | "etc": [ 135 | { 136 | "quantity": 999, 137 | "name": "Pearl of Darkness", 138 | "position": 0, 139 | "vnum": 2094 140 | }, 141 | { 142 | "quantity": 112, 143 | "name": "Small Bean", 144 | "position": 5, 145 | "vnum": 2016 146 | } 147 | ], 148 | "gold": 1000000 149 | } 150 | } 151 | ``` 152 | 153 | * Skills information 154 | 155 | ```json 156 | { 157 | "type": 18, 158 | "skills": [ 159 | { 160 | "name": "Shuriken Attack", 161 | "vnum": 880, 162 | "id": 0, 163 | "range": 3, 164 | "area": 0, 165 | "mana_cost": 0, 166 | "is_ready": true 167 | }, 168 | { 169 | "name": "Summon Kamapakun", 170 | "vnum": 886, 171 | "id": 6, 172 | "area": 3, 173 | "mana_cost": 0, 174 | "is_ready": true 175 | } 176 | ] 177 | } 178 | ``` 179 | 180 | * Map entities 181 | 182 | ```json 183 | { 184 | "type": 19, 185 | "players": [ 186 | { 187 | "name": "Hatz", 188 | "id": 123123, 189 | "x": 10, 190 | "y": 20, 191 | "family": "PhoenixEnjoyers", 192 | "hp_percent": 100, 193 | "mp_percent": 100, 194 | "level": 99, 195 | "champion_level": 80 196 | } 197 | ], 198 | "npcs": [ 199 | { 200 | "name": "Malcolm Mix", 201 | "id": 123, 202 | "x": 5, 203 | "y": 50, 204 | "vnum": 321, 205 | "hp_percent": 100, 206 | "mp_percent": 100 207 | } 208 | ], 209 | "monsters": [ 210 | { 211 | "name": "Dander", 212 | "id": 2400, 213 | "x": 43, 214 | "y": 65, 215 | "vnum": 1, 216 | "hp_percent": 100, 217 | "mp_percent": 100 218 | } 219 | ], 220 | "items": [ 221 | { 222 | "name": "Gillion Stone", 223 | "vnum": 123, 224 | "x": 69, 225 | "y": 69, 226 | "quantity": 999, 227 | "owner_id": 56789, 228 | "id": 45645 229 | } 230 | ] 231 | } 232 | ``` 233 | 234 | ## Messages sent from the CLIENT to the BOT 235 | 236 | You can send multiple messages to the bot to send or receive a packet and also do some actions like attacking, walking, picking up an item, etc. All this actions are performed by calling game functions in the main game thread to guarantee thread safety. 237 | 238 | Examples: 239 | 240 | * `Description`: Send a packet to the game server. 241 | 242 | * `packet`: The packet to send to the game server, in this case it uses an item from the inventory 243 | 244 | ```json 245 | { 246 | "type": 0, 247 | "packet": "u_i 1 1234 2 15 0 0" 248 | } 249 | ``` 250 | 251 | ___ 252 | 253 | * `Description`: Simulates a received packet from the game server. 254 | 255 | * `packet`: The packet to get received, in this case it simulates client side getting 1kkk of gold. 256 | 257 | ```json 258 | { 259 | "type": 1, 260 | "packet": "gold 1000000000 0" 261 | } 262 | ``` 263 | 264 | ___ 265 | 266 | * `Description`: Attack a monster by using the top ready skill that you've set in the bot for the player, pet and partner. 267 | 268 | * `monster_id`: ID of the monster you want to attack. 269 | 270 | ```json 271 | { 272 | "type": 2, 273 | "monster_id": 2307 274 | } 275 | ``` 276 | 277 | ___ 278 | 279 | * `Description`: Attack a monster with the given skill. 280 | 281 | * `monster_id`: ID of the monster you want to attack 282 | 283 | * `skill_id`: ID of the skill you want to use. 284 | 285 | ```json 286 | { 287 | "type": 3, 288 | "monster_id": 2307, 289 | "skill_id": 0 290 | } 291 | ``` 292 | 293 | ___ 294 | 295 | * `Description`: Character walks to coordiantes (x, y). 296 | 297 | ```json 298 | { 299 | "type": 4, 300 | "x": 34, 301 | "y": 68, 302 | } 303 | ``` 304 | 305 | ___ 306 | 307 | * `Description`: Attack a monster with the given pet skill. 308 | 309 | * `monster_id`: ID of the monster you want to attack 310 | 311 | * `skill_id`: ID of the pet skill you want to use. 312 | 313 | ```json 314 | { 315 | "type": 5, 316 | "monster_id": 2307, 317 | "skill_id": 0 318 | } 319 | ``` 320 | 321 | ___ 322 | 323 | * `Description`: Attack a monster with the given partner skill. 324 | 325 | * `monster_id`: ID of the monster you want to attack 326 | 327 | * `skill_id`: ID of the partner skill you want to use. 328 | 329 | ```json 330 | { 331 | "type": 6, 332 | "monster_id": 2307, 333 | "skill_id": 1 334 | } 335 | ``` 336 | 337 | ___ 338 | 339 | * `Description`: Pet and partner walks to coordinates (x, y). 340 | 341 | ```json 342 | { 343 | "type": 7, 344 | "x": 120, 345 | "y": 46 346 | } 347 | ``` 348 | 349 | ___ 350 | 351 | * `Description`: Walk and pick up the given item. 352 | 353 | * `item_id`: ID of the item you want to pick up. 354 | 355 | ```json 356 | { 357 | "type": 8, 358 | "item_id": 1234 359 | } 360 | ``` 361 | 362 | ____ 363 | 364 | * `Description`: Walk and collect a npc (ice flowers and that kind of stuff). 365 | 366 | * `npc_id`: ID of the npc you want to collect. 367 | 368 | ```json 369 | { 370 | "type": 9, 371 | "npc_id": 4321 372 | } 373 | ``` 374 | 375 | ___ 376 | 377 | * `Description`: Start the farming bot 378 | 379 | ```json 380 | { 381 | "type": 10 382 | } 383 | ``` 384 | 385 | ___ 386 | 387 | * `Description`: Stop the farming bot 388 | 389 | ```json 390 | { 391 | "type": 11 392 | } 393 | ``` 394 | 395 | ___ 396 | 397 | * `Description`: Continue the farming bot 398 | 399 | ```json 400 | { 401 | "type": 12 402 | } 403 | ``` 404 | 405 | ___ 406 | 407 | * `Description`: Load existing bot settings/profile 408 | 409 | * `path`: Full path of the .ini file 410 | 411 | ```json 412 | { 413 | "type": 13, 414 | "path": "C:/Program Files (x86)/Nostale/mysettings.ini" 415 | } 416 | ``` 417 | 418 | ___ 419 | 420 | * `Description`: Starts the minigame bot 421 | 422 | ```json 423 | { 424 | "type": 14 425 | } 426 | ``` 427 | 428 | ___ 429 | 430 | * `Description`: Stops the minigame bot 431 | 432 | ```json 433 | { 434 | "type": 15 435 | } 436 | ``` 437 | 438 | ___ 439 | 440 | * `Description`: Query information from the player, see response example above 441 | 442 | ```json 443 | { 444 | "type": 16 445 | } 446 | ``` 447 | 448 | ___ 449 | 450 | * `Description`: Query the inventory information from the player, see response example above 451 | 452 | ```json 453 | { 454 | "type": 17 455 | } 456 | ``` 457 | 458 | ___ 459 | 460 | * `Description`: Query the skill information from the player, see response example above 461 | 462 | ```json 463 | { 464 | "type": 18 465 | } 466 | ``` 467 | 468 | ___ 469 | 470 | * `Description`: Query information about all the map entities, see response example above 471 | 472 | ```json 473 | { 474 | "type": 19 475 | } 476 | ``` 477 | 478 | ___ 479 | 480 | * `Description` : Target an entity in the game client 481 | 482 | * `entity_id`: The id of the entity to target 483 | 484 | * `entity_type`: The type of entity (1 - player, 2 - npc, 3 - monster) 485 | 486 | ```json 487 | { 488 | "type": 20, 489 | "entity_type": 3, 490 | "entity_id": 2664 491 | } 492 | ``` 493 | 494 | ____ 495 | 496 | ## About the C++ API 497 | 498 | To use this API you just need to add the following files into your project and `#include "Api.h"` where you want to use it: 499 | 500 | * `Api.h` 501 | 502 | * `Api.cpp` 503 | 504 | * `Port_finder.h` 505 | 506 | * `Port_finder.cpp` 507 | 508 | * `Safe_queue.h` 509 | 510 | * `Safe_queue.cpp` 511 | 512 | This example was made using Microsoft Visual Studio Community 2019 and C++14. 513 | 514 | Since C++ doesn't support JSON natively you'll need to use a third party library to use it. In this example I've used [nlohmann json](https://github.com/nlohmann/json). To handle the dependencies I've used [vcpkg](https://github.com/microsoft/vcpkg). 515 | 516 | In the file [main.cpp](PhoenixAPI/main.cpp) you have an example of a packetlogger that will connect to the first port found and print to the standard output every send/recv packet that comes from the bot. 517 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31911.196 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PhoenixAPI", "PhoenixAPI\PhoenixAPI.vcxproj", "{E00B6134-115F-47D4-BC74-A94762C64321}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Ts26_Bot", "examples\Ts26_Bot\Ts26_Bot.vcxproj", "{F54CAF92-4743-4EDD-A619-21E6C3D98905}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InstantCombat", "examples\InstantCombat\InstantCombat.vcxproj", "{D2406D29-2C99-4A06-AA29-4767AEE48F06}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {E00B6134-115F-47D4-BC74-A94762C64321}.Debug|x64.ActiveCfg = Debug|x64 21 | {E00B6134-115F-47D4-BC74-A94762C64321}.Debug|x64.Build.0 = Debug|x64 22 | {E00B6134-115F-47D4-BC74-A94762C64321}.Debug|x86.ActiveCfg = Debug|Win32 23 | {E00B6134-115F-47D4-BC74-A94762C64321}.Debug|x86.Build.0 = Debug|Win32 24 | {E00B6134-115F-47D4-BC74-A94762C64321}.Release|x64.ActiveCfg = Release|x64 25 | {E00B6134-115F-47D4-BC74-A94762C64321}.Release|x64.Build.0 = Release|x64 26 | {E00B6134-115F-47D4-BC74-A94762C64321}.Release|x86.ActiveCfg = Release|Win32 27 | {E00B6134-115F-47D4-BC74-A94762C64321}.Release|x86.Build.0 = Release|Win32 28 | {F54CAF92-4743-4EDD-A619-21E6C3D98905}.Debug|x64.ActiveCfg = Debug|x64 29 | {F54CAF92-4743-4EDD-A619-21E6C3D98905}.Debug|x64.Build.0 = Debug|x64 30 | {F54CAF92-4743-4EDD-A619-21E6C3D98905}.Debug|x86.ActiveCfg = Debug|Win32 31 | {F54CAF92-4743-4EDD-A619-21E6C3D98905}.Debug|x86.Build.0 = Debug|Win32 32 | {F54CAF92-4743-4EDD-A619-21E6C3D98905}.Release|x64.ActiveCfg = Release|x64 33 | {F54CAF92-4743-4EDD-A619-21E6C3D98905}.Release|x64.Build.0 = Release|x64 34 | {F54CAF92-4743-4EDD-A619-21E6C3D98905}.Release|x86.ActiveCfg = Release|Win32 35 | {F54CAF92-4743-4EDD-A619-21E6C3D98905}.Release|x86.Build.0 = Release|Win32 36 | {D2406D29-2C99-4A06-AA29-4767AEE48F06}.Debug|x64.ActiveCfg = Debug|x64 37 | {D2406D29-2C99-4A06-AA29-4767AEE48F06}.Debug|x64.Build.0 = Debug|x64 38 | {D2406D29-2C99-4A06-AA29-4767AEE48F06}.Debug|x86.ActiveCfg = Debug|Win32 39 | {D2406D29-2C99-4A06-AA29-4767AEE48F06}.Debug|x86.Build.0 = Debug|Win32 40 | {D2406D29-2C99-4A06-AA29-4767AEE48F06}.Release|x64.ActiveCfg = Release|x64 41 | {D2406D29-2C99-4A06-AA29-4767AEE48F06}.Release|x64.Build.0 = Release|x64 42 | {D2406D29-2C99-4A06-AA29-4767AEE48F06}.Release|x86.ActiveCfg = Release|Win32 43 | {D2406D29-2C99-4A06-AA29-4767AEE48F06}.Release|x86.Build.0 = Release|Win32 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {84D580D9-AA95-461E-A529-57BFD6BEE3C1} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/Api.cpp: -------------------------------------------------------------------------------- 1 | #include "Api.h" 2 | 3 | int Phoenix::Api::instance_counter = 0; 4 | 5 | Phoenix::Api::Api(int port) : run(true) 6 | { 7 | initialize_socket(port); 8 | ++instance_counter; 9 | recv_thread = std::thread(&Api::receive_messages, this); 10 | } 11 | 12 | Phoenix::Api::~Api() 13 | { 14 | if (recv_thread.joinable()) 15 | { 16 | run = false; 17 | recv_thread.join(); 18 | } 19 | 20 | --instance_counter; 21 | closesocket(sock); 22 | 23 | if (instance_counter == 0) 24 | { 25 | WSACleanup(); 26 | } 27 | } 28 | 29 | int Phoenix::Api::send_data(const std::string& data) 30 | { 31 | std::string msg = data + '\1'; 32 | return send(sock, msg.c_str(), msg.size(), 0); 33 | } 34 | 35 | bool Phoenix::Api::empty() 36 | { 37 | return messages.empty(); 38 | } 39 | 40 | std::string Phoenix::Api::get_message() 41 | { 42 | if (messages.empty()) 43 | { 44 | return std::string(); 45 | } 46 | 47 | std::string msg = messages.front(); 48 | messages.pop(); 49 | 50 | return msg; 51 | } 52 | 53 | bool Phoenix::Api::send_packet(const std::string& packet) 54 | { 55 | nlohmann::json json_data; 56 | json_data["type"] = Type::packet_send; 57 | json_data["packet"] = packet; 58 | 59 | return (send_data(json_data.dump()) != SOCKET_ERROR); 60 | } 61 | 62 | bool Phoenix::Api::recv_packet(const std::string& packet) 63 | { 64 | nlohmann::json json_data; 65 | json_data["type"] = Type::packet_recv; 66 | json_data["packet"] = packet; 67 | 68 | return (send_data(json_data.dump()) != SOCKET_ERROR); 69 | } 70 | 71 | bool Phoenix::Api::attack_monster(int monster_id) 72 | { 73 | nlohmann::json json_data; 74 | json_data["type"] = Type::attack; 75 | json_data["monster_id"] = monster_id; 76 | 77 | return (send_data(json_data.dump()) != SOCKET_ERROR); 78 | } 79 | 80 | bool Phoenix::Api::use_player_skill(int monster_id, int skill_id) 81 | { 82 | nlohmann::json json_data; 83 | json_data["type"] = Type::player_skill; 84 | json_data["monster_id"] = monster_id; 85 | json_data["skill_id"] = skill_id; 86 | 87 | return (send_data(json_data.dump()) != SOCKET_ERROR); 88 | } 89 | 90 | bool Phoenix::Api::player_walk(int x, int y) 91 | { 92 | nlohmann::json json_data; 93 | json_data["type"] = Type::player_walk; 94 | json_data["x"] = x; 95 | json_data["y"] = y; 96 | 97 | return (send_data(json_data.dump()) != SOCKET_ERROR); 98 | } 99 | 100 | bool Phoenix::Api::use_pet_skill(int monster_id, int skill_id) 101 | { 102 | nlohmann::json json_data; 103 | json_data["type"] = Type::pet_skill; 104 | json_data["monster_id"] = monster_id; 105 | json_data["skill_id"] = skill_id; 106 | 107 | return (send_data(json_data.dump()) != SOCKET_ERROR); 108 | } 109 | 110 | bool Phoenix::Api::use_partner_skill(int monster_id, int skill_id) 111 | { 112 | nlohmann::json json_data; 113 | json_data["type"] = Type::partner_skill; 114 | json_data["monster_id"] = monster_id; 115 | json_data["skill_id"] = skill_id; 116 | 117 | return (send_data(json_data.dump()) != SOCKET_ERROR); 118 | } 119 | 120 | bool Phoenix::Api::pets_walk(int x, int y) 121 | { 122 | nlohmann::json json_data; 123 | json_data["type"] = Type::pets_walk; 124 | json_data["x"] = x; 125 | json_data["y"] = y; 126 | 127 | return (send_data(json_data.dump()) != SOCKET_ERROR); 128 | } 129 | 130 | bool Phoenix::Api::pick_up(int item_id) 131 | { 132 | nlohmann::json json_data; 133 | json_data["type"] = Type::pick_up; 134 | json_data["item_id"] = item_id; 135 | 136 | return (send_data(json_data.dump()) != SOCKET_ERROR); 137 | } 138 | 139 | bool Phoenix::Api::collect(int npc_id) 140 | { 141 | nlohmann::json json_data; 142 | json_data["type"] = Type::collect; 143 | json_data["npc_id"] = npc_id; 144 | 145 | return (send_data(json_data.dump()) != SOCKET_ERROR); 146 | } 147 | 148 | bool Phoenix::Api::start_bot() 149 | { 150 | nlohmann::json json_data; 151 | json_data["type"] = Type::start_bot; 152 | 153 | return (send_data(json_data.dump()) != SOCKET_ERROR); 154 | } 155 | 156 | bool Phoenix::Api::stop_bot() 157 | { 158 | nlohmann::json json_data; 159 | json_data["type"] = Type::stop_bot; 160 | 161 | return (send_data(json_data.dump()) != SOCKET_ERROR); 162 | } 163 | 164 | bool Phoenix::Api::continue_bot() 165 | { 166 | nlohmann::json json_data; 167 | json_data["type"] = Type::continue_bot; 168 | 169 | return (send_data(json_data.dump()) != SOCKET_ERROR); 170 | } 171 | 172 | bool Phoenix::Api::load_settings(const std::string& settings_path) 173 | { 174 | nlohmann::json json_data; 175 | json_data["type"] = Type::load_settings; 176 | json_data["path"] = settings_path; 177 | 178 | return (send_data(json_data.dump()) != SOCKET_ERROR); 179 | } 180 | 181 | bool Phoenix::Api::start_minigame_bot() 182 | { 183 | nlohmann::json json_data; 184 | json_data["type"] = Type::start_minigame_bot; 185 | 186 | return (send_data(json_data.dump()) != SOCKET_ERROR); 187 | } 188 | 189 | bool Phoenix::Api::stop_minigame_bot() 190 | { 191 | nlohmann::json json_data; 192 | json_data["type"] = Type::stop_minigame_bot; 193 | 194 | return (send_data(json_data.dump()) != SOCKET_ERROR); 195 | } 196 | 197 | bool Phoenix::Api::query_player_info() 198 | { 199 | nlohmann::json json_data; 200 | json_data["type"] = Type::query_player_info; 201 | 202 | return (send_data(json_data.dump()) != SOCKET_ERROR); 203 | } 204 | 205 | bool Phoenix::Api::query_inventory() 206 | { 207 | nlohmann::json json_data; 208 | json_data["type"] = Type::query_inventory; 209 | 210 | return (send_data(json_data.dump()) != SOCKET_ERROR); 211 | } 212 | 213 | bool Phoenix::Api::query_skills_info() 214 | { 215 | nlohmann::json json_data; 216 | json_data["type"] = Type::query_skills_info; 217 | 218 | return (send_data(json_data.dump()) != SOCKET_ERROR); 219 | } 220 | 221 | bool Phoenix::Api::query_map_entities() 222 | { 223 | nlohmann::json json_data; 224 | json_data["type"] = Type::query_map_entities; 225 | 226 | return (send_data(json_data.dump()) != SOCKET_ERROR); 227 | } 228 | 229 | bool Phoenix::Api::target_entity(int entity_id, int entity_type) 230 | { 231 | nlohmann::json json_data; 232 | json_data["type"] = Type::target_entity; 233 | json_data["entity_id"] = entity_id; 234 | json_data["entity_type"] = entity_type; 235 | 236 | return (send_data(json_data.dump()) != SOCKET_ERROR); 237 | } 238 | 239 | void Phoenix::Api::receive_messages() 240 | { 241 | constexpr int buffer_size = 4096; 242 | char buffer[buffer_size]; 243 | std::string data; 244 | size_t delim_pos; 245 | 246 | while (run) 247 | { 248 | memset(buffer, 0, buffer_size); 249 | 250 | if (recv(sock, buffer, buffer_size - 1, 0) <= 0) 251 | { 252 | std::cerr << "recv failed\n"; 253 | run = false; 254 | break; 255 | } 256 | 257 | data += buffer; 258 | 259 | while ((delim_pos = data.find('\1')) != std::string::npos) 260 | { 261 | std::string message = data.substr(0, delim_pos - 1); 262 | data.erase(0, delim_pos + 1); 263 | 264 | messages.push(message); 265 | } 266 | } 267 | } 268 | 269 | void Phoenix::Api::initialize_socket(int port) 270 | { 271 | if (instance_counter == 0) 272 | { 273 | WSADATA wsa_data; 274 | 275 | if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) 276 | { 277 | std::cerr << "WSAStartup failed\n"; 278 | exit(-1); 279 | } 280 | } 281 | 282 | sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 283 | 284 | if (sock == INVALID_SOCKET) 285 | { 286 | std::cerr << "socket failed\n"; 287 | exit(-1); 288 | } 289 | 290 | sockaddr_in addr; 291 | inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); 292 | addr.sin_family = AF_INET; 293 | addr.sin_port = htons(port); 294 | 295 | if (connect(sock, reinterpret_cast(&addr), sizeof(sockaddr_in)) != 0) 296 | { 297 | std::cerr << "connect failed\n"; 298 | closesocket(sock); 299 | WSACleanup(); 300 | exit(-1); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/Api.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Api.h 3 | * @brief Main file of the Phoenix Bot API 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include "Port_finder.h" 12 | #include "Safe_queue.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #pragma comment(lib, "ws2_32.lib") 20 | 21 | namespace Phoenix 22 | { 23 | /** 24 | * @brief Represents the type of messages that we can send/receive from the bot. 25 | */ 26 | enum class Type 27 | { 28 | packet_send, 29 | packet_recv, 30 | attack, 31 | player_skill, 32 | player_walk, 33 | pet_skill, 34 | partner_skill, 35 | pets_walk, 36 | pick_up, 37 | collect, 38 | start_bot, 39 | stop_bot, 40 | continue_bot, 41 | load_settings, 42 | start_minigame_bot, 43 | stop_minigame_bot, 44 | query_player_info, 45 | query_inventory, 46 | query_skills_info, 47 | query_map_entities, 48 | target_entity 49 | }; 50 | 51 | /** 52 | * @brief Class that handles the main logic of the API. 53 | * It connects to the given port and receive the messages from 54 | * the bot in a thread safe queue. 55 | */ 56 | class Api 57 | { 58 | public: 59 | /** 60 | * @brief Constructor. 61 | * @param port The port of the bot you want to connect 62 | */ 63 | explicit Api(int port); 64 | 65 | /** 66 | * @brief Destructor. 67 | */ 68 | ~Api(); 69 | 70 | /** 71 | * @brief Send data to the bot. 72 | * @param data The data to send, it must be a valid JSON string 73 | * @return If no error then returns the number of bytes sent, otherwise it returns SOCKET_ERROR 74 | */ 75 | int send_data(const std::string& data); 76 | 77 | /** 78 | * @brief Check if there is any message to read from the port. 79 | * @return true or false 80 | */ 81 | bool empty(); 82 | 83 | /** 84 | * @brief Get a message from the bot and pops it from the queue. 85 | * @return The first message in the queue. 86 | */ 87 | std::string get_message(); 88 | 89 | /** 90 | * @brief Wrapper to send a packet 91 | * @param packet The packet you want to send 92 | * @return false if there's any error, true otherwise 93 | */ 94 | bool send_packet(const std::string& packet); 95 | 96 | /** 97 | * @brief Wrapper to receive a packet 98 | * @param packet The packet you want to receive 99 | * @return false if there's any error, true otherwise 100 | */ 101 | bool recv_packet(const std::string& packet); 102 | 103 | /** 104 | * @brief Wrapper for attacking a monster with the skills set 105 | * in the bot 106 | * @param monster_id The id of the monster to attack 107 | * @return false if there's any error, true otherwise 108 | */ 109 | bool attack_monster(int monster_id); 110 | 111 | /** 112 | * @brief Wrapper for attacking a monster with a specific skill 113 | * @param monster_id The id of the monster to attack 114 | * @param skill_id The id of the skill to use 115 | * @return false if there's any error, true otherwise 116 | */ 117 | bool use_player_skill(int monster_id, int skill_id); 118 | 119 | /** 120 | * @brief Moves the player to a specific position 121 | * @return false if there's any error, true otherwise 122 | */ 123 | bool player_walk(int x, int y); 124 | 125 | /** 126 | * @brief Attack a monster with a specific pet skill 127 | * @param monster_id The id of the monster to attack 128 | * @param skill_id The id of the skill to use 129 | * @return false if there's any error, true otherwise 130 | */ 131 | bool use_pet_skill(int monster_id, int skill_id); 132 | 133 | /** 134 | * @brief Attack a monster with a specific partner skill 135 | * @param monster_id The id of the monster to attack 136 | * @param skill_id The id of the skill to use 137 | * @return false if there's any error, true otherwise 138 | */ 139 | bool use_partner_skill(int monster_id, int skill_id); 140 | 141 | /** 142 | * @brief Moves both the pet and partner to a specific position 143 | * @return false if there's any error, true otherwise 144 | */ 145 | bool pets_walk(int x, int y); 146 | 147 | /** 148 | * @brief Walk and pick up an item 149 | * @param item_id The id of the item to pick up 150 | * @return false if there's any error, true otherwise 151 | */ 152 | bool pick_up(int item_id); 153 | 154 | /** 155 | * @brief Collect an npc (like ice flowers, grass, etc) 156 | * @param npc_id The id of the npc to collect 157 | * @return false if there's any error, true otherwise 158 | */ 159 | bool collect(int npc_id); 160 | 161 | /** 162 | * @brief Start the farming bot 163 | */ 164 | bool start_bot(); 165 | 166 | /** 167 | * @brief Stop the farming bot 168 | */ 169 | bool stop_bot(); 170 | 171 | /** 172 | * @brief Continue the farming bot 173 | */ 174 | bool continue_bot(); 175 | 176 | /** 177 | * @brief Load a profile into the bot 178 | * @param settings_path The path to the .ini file 179 | * @return false if there's any error, true otherwise 180 | */ 181 | bool load_settings(const std::string& settings_path); 182 | 183 | /** 184 | * @brief Starts the minigame bot 185 | * @return false if there's any error, true otherwise 186 | */ 187 | bool start_minigame_bot(); 188 | 189 | /** 190 | * @brief Stops the minigame bot 191 | * @return false if there's any error, true otherwise 192 | */ 193 | bool stop_minigame_bot(); 194 | 195 | /** 196 | * @brief Query player information from the bot 197 | * @return false if there's any error, true otherwise 198 | */ 199 | bool query_player_info(); 200 | 201 | /** 202 | * @brief Query the inventory information from the bot 203 | * @return false if there's any error, true otherwise 204 | */ 205 | bool query_inventory(); 206 | 207 | /** 208 | * @brief Query player skills information from the bot 209 | * @return false if there's any error, true otherwise 210 | */ 211 | bool query_skills_info(); 212 | 213 | /** 214 | * @brief Query all the map entities from the bot 215 | * @return false if there's any error, true otherwise 216 | */ 217 | bool query_map_entities(); 218 | 219 | /** 220 | * @brief Target an entity client side. 221 | * @param entity_id The id of the entity that you want to target 222 | * @param entity_type The type of the entity that you want to target (1 - player, 2 - npc, 3 - monster) 223 | */ 224 | bool target_entity(int entity_id, int entity_type); 225 | 226 | private: 227 | /** 228 | * @brief Function that will be receiving all the messages 229 | * from the bot in a different thread. 230 | */ 231 | void receive_messages(); 232 | 233 | /** 234 | * @brief Initialize a socket connected to the given port 235 | * @param port The port to connect 236 | */ 237 | void initialize_socket(int port); 238 | 239 | static int instance_counter; 240 | SOCKET sock; 241 | Safe_queue messages; 242 | std::thread recv_thread; 243 | bool run; 244 | }; 245 | } 246 | 247 | 248 | 249 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/PhoenixAPI.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {e00b6134-115f-47d4-bc74-a94762c64321} 25 | PhoenixAPI 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | stdcpp14 92 | 93 | 94 | Console 95 | true 96 | 97 | 98 | 99 | 100 | Level3 101 | true 102 | true 103 | true 104 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | true 106 | 107 | 108 | Console 109 | true 110 | true 111 | true 112 | 113 | 114 | 115 | 116 | Level3 117 | true 118 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 119 | true 120 | 121 | 122 | Console 123 | true 124 | 125 | 126 | 127 | 128 | Level3 129 | true 130 | true 131 | true 132 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 133 | true 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/PhoenixAPI.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Archivos de origen 20 | 21 | 22 | Archivos de origen 23 | 24 | 25 | Archivos de origen 26 | 27 | 28 | Archivos de origen 29 | 30 | 31 | 32 | 33 | Archivos de encabezado 34 | 35 | 36 | Archivos de encabezado 37 | 38 | 39 | Archivos de encabezado 40 | 41 | 42 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/Port_finder.cpp: -------------------------------------------------------------------------------- 1 | #include "Port_finder.h" 2 | #include 3 | #include 4 | 5 | static std::vector ports; 6 | 7 | BOOL CALLBACK enum_windows_callback(HWND hwnd, LPARAM param) 8 | { 9 | const int title_size = GetWindowTextLengthA(hwnd); 10 | 11 | if (title_size <= 0) 12 | { 13 | return TRUE; 14 | } 15 | 16 | char* title = new char[title_size + 1]; 17 | GetWindowTextA(hwnd, title, title_size + 1); 18 | 19 | std::string title_wrapper(title); 20 | 21 | if (title_wrapper.find("] - Phoenix Bot:") == std::string::npos) 22 | { 23 | delete[] title; 24 | return TRUE; 25 | } 26 | 27 | std::string port = title_wrapper.substr(title_wrapper.find_last_of(':') + 1); 28 | 29 | if (port.empty()) 30 | { 31 | delete[] title; 32 | return TRUE; 33 | } 34 | 35 | ports.push_back(std::stoi(port)); 36 | 37 | delete[] title; 38 | return TRUE; 39 | } 40 | 41 | std::vector Phoenix::find_ports() 42 | { 43 | ports.clear(); 44 | 45 | EnumWindows(enum_windows_callback, 0); 46 | 47 | return ports; 48 | } 49 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/Port_finder.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Port_finder.h 3 | * @brief Additional API function to get the ports 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | namespace Phoenix 14 | { 15 | /** 16 | * @brief Find the ports from all the running bots 17 | * @return std::vector with the ports 18 | */ 19 | std::vector find_ports(); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/Safe_queue.cpp: -------------------------------------------------------------------------------- 1 | #include "Safe_queue.h" 2 | 3 | void Phoenix::Safe_queue::push(const std::string& message) 4 | { 5 | size_t length = message.size(); 6 | char* message_copy = new char[length + 1]; 7 | memcpy(message_copy, message.c_str(), length); 8 | message_copy[length] = '\0'; 9 | 10 | std::lock_guard lock(mutex); 11 | queue.push(message_copy); 12 | } 13 | 14 | void Phoenix::Safe_queue::pop() 15 | { 16 | std::lock_guard lock(mutex); 17 | char* message = queue.front(); 18 | queue.pop(); 19 | delete[] message; 20 | } 21 | 22 | std::string Phoenix::Safe_queue::front() 23 | { 24 | std::lock_guard lock(mutex); 25 | char* message = queue.front(); 26 | 27 | return std::string(message); 28 | } 29 | 30 | bool Phoenix::Safe_queue::empty() 31 | { 32 | std::lock_guard lock(mutex); 33 | return queue.empty(); 34 | } 35 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/Safe_queue.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Safe_queue.h 3 | * @brief Safe thread implementation of a std::queue 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace Phoenix 16 | { 17 | /** 18 | * @brief Thread safe char* queue 19 | */ 20 | class Safe_queue 21 | { 22 | public: 23 | void push(const std::string& message); 24 | 25 | void pop(); 26 | 27 | std::string front(); 28 | 29 | bool empty(); 30 | 31 | private: 32 | std::mutex mutex; 33 | std::queue queue; 34 | }; 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /cpp/PhoenixAPI/main.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file main.cpp 3 | * @brief Packetlogger example using the API 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include "Api.h" 12 | 13 | int main() 14 | { 15 | using json = nlohmann::json; 16 | 17 | std::vector ports = Phoenix::find_ports(); 18 | 19 | if (ports.size() <= 0) 20 | { 21 | std::cerr << "Find ports failed\n"; 22 | return -1; 23 | } 24 | 25 | Phoenix::Api api(ports.front()); 26 | 27 | while (true) 28 | { 29 | while (!api.empty()) 30 | { 31 | std::string message = api.get_message(); 32 | 33 | try 34 | { 35 | json json_msg = json::parse(message); 36 | 37 | if (json_msg["type"] == Phoenix::Type::packet_send) 38 | { 39 | std::cout << "[SEND]: " << std::string(json_msg["packet"]) << std::endl; 40 | } 41 | 42 | if (json_msg["type"] == Phoenix::Type::packet_recv) 43 | { 44 | std::cout << "[RECV]: " << std::string(json_msg["packet"]) << std::endl; 45 | } 46 | } 47 | 48 | catch (const std::exception& e) 49 | { 50 | std::cerr << e.what() << std::endl; 51 | std::cin.get(); 52 | return 1; 53 | } 54 | } 55 | 56 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Bot.cpp: -------------------------------------------------------------------------------- 1 | #include "Bot.h" 2 | #include 3 | #include 4 | #include 5 | 6 | Bot::Bot(Phoenix::Api* api, Scene* scene) 7 | : api(api) 8 | , scene(scene) 9 | , point({ 0, 0 }) 10 | , round(0) 11 | , afk_last_round(true) 12 | , move_to_players(true) 13 | , move_to_point(false) 14 | , moving(false) 15 | , waiting_for_spawn(false) 16 | , current_map_id(0) 17 | { 18 | time_begin = clock(); 19 | generator = std::mt19937(std::chrono::system_clock::now().time_since_epoch().count()); 20 | load_config(); 21 | } 22 | 23 | void Bot::on_recv(const std::vector& packet_splitted, const std::string& full_packet) 24 | { 25 | std::string header = packet_splitted[0]; 26 | 27 | if (header == "qnamli") 28 | handle_qnamli(packet_splitted, full_packet); 29 | 30 | if (header == "msgi") 31 | handle_msgi(packet_splitted, full_packet); 32 | 33 | if (header == "c_map") 34 | handle_cmap(packet_splitted, full_packet); 35 | 36 | if (header == "in") 37 | handle_in(packet_splitted, full_packet); 38 | } 39 | 40 | void Bot::handle_in(const std::vector& packet_splitted, const std::string& full_packet) 41 | { 42 | if (current_map_id == ic_map_id && waiting_for_spawn) 43 | { 44 | waiting_for_spawn = false; 45 | 46 | if (moving) 47 | moving = false; 48 | } 49 | } 50 | 51 | void Bot::handle_qnamli(const std::vector& packet_splitted, const std::string& full_packet) 52 | { 53 | if (packet_splitted.size() < 7) 54 | return; 55 | 56 | if (packet_splitted[2] == "#guri^506" && packet_splitted[3] == "379") 57 | { 58 | std::uniform_int_distribution<> dist(1000, 10000); 59 | int random_delay = dist(generator); 60 | 61 | std::thread([this, random_delay, packet_splitted]() { 62 | std::stringstream ss; 63 | ss << "Joining instant combat with " << random_delay << " ms delay" << std::endl; 64 | Logger::print(ss.str()); 65 | std::this_thread::sleep_for(std::chrono::milliseconds(random_delay)); 66 | api->send_packet("guri 508"); 67 | std::uniform_int_distribution<> d(1000, 2000); 68 | std::this_thread::sleep_for(std::chrono::milliseconds(d(generator))); 69 | api->send_packet(packet_splitted[2]); 70 | }).detach(); 71 | 72 | } 73 | } 74 | 75 | void Bot::handle_msgi(const std::vector& packet_splitted, const std::string& full_packet) 76 | { 77 | if (packet_splitted.size() < 8) 78 | return; 79 | 80 | // Here are the monsters! || Monsters are coming in mase 81 | if (full_packet == "msgi 0 387 0 0 0 0 0" || full_packet == "msgi 0 384 0 0 0 0 0") 82 | { 83 | moving = false; 84 | 85 | if (afk_last_round) 86 | { 87 | if (round != 4) 88 | api->start_bot(); 89 | } 90 | else 91 | { 92 | api->start_bot(); 93 | } 94 | } 95 | 96 | // Monsters will appear in 40 seconds 97 | if (full_packet == "msgi 0 1287 4 40 0 0 0") 98 | { 99 | std::stringstream ss; 100 | ss << "Round " << round << " finished" << std::endl; 101 | Logger::print(ss.str()); 102 | 103 | api->stop_bot(); 104 | ++round; 105 | 106 | std::uniform_int_distribution<> dist(1000, 10000); 107 | int random_delay = dist(generator); 108 | 109 | std::thread([this, random_delay, packet_splitted]() { 110 | std::this_thread::sleep_for(std::chrono::milliseconds(random_delay)); 111 | moving = true; 112 | }).detach(); 113 | 114 | 115 | 116 | } 117 | 118 | if (full_packet == "msgi 0 383 0 0 0 0 0") 119 | { 120 | Logger::print("You've won the instant combat\n"); 121 | } 122 | } 123 | 124 | void Bot::handle_cmap(const std::vector& packet_splitted, const std::string& full_packet) 125 | { 126 | if (packet_splitted.size() < 4) 127 | return; 128 | 129 | int new_map; 130 | int map_id; 131 | 132 | try 133 | { 134 | new_map = std::stoi(packet_splitted[3]); 135 | map_id = std::stoi(packet_splitted[2]); 136 | 137 | if (new_map == 0) 138 | return; 139 | 140 | current_map_id = map_id; 141 | 142 | if (map_id == ic_map_id) 143 | { 144 | std::uniform_int_distribution<> dist(1000, 10000); 145 | int random_delay = dist(generator); 146 | 147 | std::thread([this, random_delay, packet_splitted]() { 148 | std::this_thread::sleep_for(std::chrono::milliseconds(random_delay)); 149 | moving = true; 150 | }).detach(); 151 | 152 | waiting_for_spawn = true; 153 | } 154 | else 155 | { 156 | std::cout << "Map is not IC map" << std::endl; 157 | waiting_for_spawn = false; 158 | moving = false; 159 | round = 0; 160 | api->stop_bot(); 161 | } 162 | } 163 | catch (const std::exception& e) 164 | { 165 | std::stringstream ss; 166 | ss << "Bot::handle_cmap " << e.what() << std::endl; 167 | ss << "Packet: " << full_packet << std::endl; 168 | 169 | Logger::error(ss.str()); 170 | } 171 | } 172 | 173 | void Bot::handle_map_entities(const nlohmann::json& data) 174 | { 175 | // Move to point/players when there are no monsters in the map 176 | if (current_map_id == ic_map_id && data["monsters"].size() == 0 && !waiting_for_spawn) { 177 | moving = true; 178 | waiting_for_spawn = true; 179 | api->stop_bot(); 180 | } 181 | } 182 | 183 | void Bot::run() 184 | { 185 | float time_diff = float(clock() - time_begin) / CLOCKS_PER_SEC; 186 | 187 | if (moving) 188 | { 189 | if (time_diff > 1.0f) 190 | { 191 | time_begin = clock(); 192 | 193 | if (move_to_players) 194 | { 195 | std::pair position = scene->get_central_player_position(); 196 | walk(position.first, position.second); 197 | } 198 | else if (move_to_point) 199 | { 200 | walk(point.first, point.second); 201 | } 202 | } 203 | } 204 | else 205 | { 206 | if (time_diff > 2.0f && current_map_id == ic_map_id) 207 | { 208 | time_begin = clock(); 209 | // Send query to check how many mobs are in the map 210 | api->query_map_entities(); 211 | } 212 | } 213 | } 214 | 215 | void Bot::walk(int x, int y) 216 | { 217 | if (x > 0 && y > 0) 218 | { 219 | api->player_walk(x, y); 220 | api->pets_walk(x, y); 221 | } 222 | } 223 | 224 | void Bot::load_config() 225 | { 226 | INIReader reader("./ic_settings.ini"); 227 | 228 | if (reader.ParseError() < 0) 229 | { 230 | Logger::error("Can't load ic_settings.ini\n"); 231 | return; 232 | } 233 | 234 | const std::string section = "Settings"; 235 | afk_last_round = reader.GetBoolean(section, "afk_last_round", true); 236 | move_to_players = reader.GetBoolean(section, "move_to_players", true); 237 | move_to_point = reader.GetBoolean(section, "move_to_point", false); 238 | point = { reader.GetInteger(section, "x", 0), reader.GetInteger(section, "y", 0) }; 239 | } 240 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Bot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Module.h" 3 | #include "Scene.h" 4 | #include "PhoenixApi/Api.h" 5 | #include "INIReader.h" 6 | #include "Logger.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class Bot : public Module 13 | { 14 | public: 15 | explicit Bot(Phoenix::Api* api, Scene* scene); 16 | 17 | void on_recv(const std::vector& packet_splitted, const std::string& full_packet); 18 | void run(); 19 | void handle_map_entities(const nlohmann::json& data); 20 | 21 | private: 22 | void handle_qnamli(const std::vector& packet_splitted, const std::string& full_packet); 23 | void handle_msgi(const std::vector& packet_splitted, const std::string& full_packet); 24 | void handle_cmap(const std::vector& packet_splitted, const std::string& full_packet); 25 | void handle_in(const std::vector& packet_splitted, const std::string& full_packet); 26 | 27 | void walk(int x, int y); 28 | void load_config(); 29 | 30 | static constexpr int ic_map_id = 2004; 31 | Phoenix::Api* api; 32 | Scene* scene; 33 | std::pair point; 34 | int round; 35 | bool afk_last_round; 36 | bool move_to_players; 37 | bool move_to_point; 38 | bool moving; 39 | bool waiting_for_spawn; 40 | int current_map_id; 41 | clock_t time_begin; 42 | std::mt19937 generator; 43 | }; -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/InstantCombat.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {d2406d29-2c99-4a06-aa29-4767aee48f06} 25 | InstantCombat 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/InstantCombat.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {6e1ea5b8-0187-48e2-b069-ba523e970d65} 18 | 19 | 20 | 21 | 22 | Source Files 23 | 24 | 25 | PhoenixApi 26 | 27 | 28 | PhoenixApi 29 | 30 | 31 | PhoenixApi 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | 47 | 48 | PhoenixApi 49 | 50 | 51 | PhoenixApi 52 | 53 | 54 | PhoenixApi 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | 3 | namespace 4 | { 5 | std::mutex mtx; 6 | } 7 | 8 | void Logger::print(const std::string& text) 9 | { 10 | std::lock_guard lock(mtx); 11 | std::cout << text; 12 | } 13 | 14 | void Logger::error(const std::string& text) 15 | { 16 | std::lock_guard lock(mtx); 17 | std::cerr << text; 18 | } 19 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace Logger 7 | { 8 | void print(const std::string& text); 9 | void error(const std::string& text); 10 | } -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "PhoenixApi/Api.h" 4 | 5 | class Module 6 | { 7 | public: 8 | virtual void on_send(const std::vector& packet_splitted, const std::string& full_packet) {} 9 | virtual void on_recv(const std::vector& packet_splitted, const std::string& full_packet) {} 10 | }; 11 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/PhoenixApi/Api.cpp: -------------------------------------------------------------------------------- 1 | #include "Api.h" 2 | 3 | int Phoenix::Api::instance_counter = 0; 4 | 5 | Phoenix::Api::Api(int port) : run(true) 6 | { 7 | initialize_socket(port); 8 | ++instance_counter; 9 | recv_thread = std::thread(&Api::receive_messages, this); 10 | } 11 | 12 | Phoenix::Api::~Api() 13 | { 14 | if (recv_thread.joinable()) 15 | { 16 | run = false; 17 | recv_thread.join(); 18 | } 19 | 20 | --instance_counter; 21 | closesocket(sock); 22 | 23 | if (instance_counter == 0) 24 | { 25 | WSACleanup(); 26 | } 27 | } 28 | 29 | int Phoenix::Api::send_data(const std::string& data) 30 | { 31 | std::string msg = data + '\1'; 32 | return send(sock, msg.c_str(), msg.size(), 0); 33 | } 34 | 35 | bool Phoenix::Api::empty() 36 | { 37 | return messages.empty(); 38 | } 39 | 40 | std::string Phoenix::Api::get_message() 41 | { 42 | if (messages.empty()) 43 | { 44 | return std::string(); 45 | } 46 | 47 | std::string msg = messages.front(); 48 | messages.pop(); 49 | 50 | return msg; 51 | } 52 | 53 | bool Phoenix::Api::send_packet(const std::string& packet) 54 | { 55 | nlohmann::json json_data; 56 | json_data["type"] = Type::packet_send; 57 | json_data["packet"] = packet; 58 | 59 | return (send_data(json_data.dump()) != SOCKET_ERROR); 60 | } 61 | 62 | bool Phoenix::Api::recv_packet(const std::string& packet) 63 | { 64 | nlohmann::json json_data; 65 | json_data["type"] = Type::packet_recv; 66 | json_data["packet"] = packet; 67 | 68 | return (send_data(json_data.dump()) != SOCKET_ERROR); 69 | } 70 | 71 | bool Phoenix::Api::attack_monster(int monster_id) 72 | { 73 | nlohmann::json json_data; 74 | json_data["type"] = Type::attack; 75 | json_data["monster_id"] = monster_id; 76 | 77 | return (send_data(json_data.dump()) != SOCKET_ERROR); 78 | } 79 | 80 | bool Phoenix::Api::use_player_skill(int monster_id, int skill_id) 81 | { 82 | nlohmann::json json_data; 83 | json_data["type"] = Type::player_skill; 84 | json_data["monster_id"] = monster_id; 85 | json_data["skill_id"] = skill_id; 86 | 87 | return (send_data(json_data.dump()) != SOCKET_ERROR); 88 | } 89 | 90 | bool Phoenix::Api::player_walk(int x, int y) 91 | { 92 | nlohmann::json json_data; 93 | json_data["type"] = Type::player_walk; 94 | json_data["x"] = x; 95 | json_data["y"] = y; 96 | 97 | return (send_data(json_data.dump()) != SOCKET_ERROR); 98 | } 99 | 100 | bool Phoenix::Api::use_pet_skill(int monster_id, int skill_id) 101 | { 102 | nlohmann::json json_data; 103 | json_data["type"] = Type::pet_skill; 104 | json_data["monster_id"] = monster_id; 105 | json_data["skill_id"] = skill_id; 106 | 107 | return (send_data(json_data.dump()) != SOCKET_ERROR); 108 | } 109 | 110 | bool Phoenix::Api::use_partner_skill(int monster_id, int skill_id) 111 | { 112 | nlohmann::json json_data; 113 | json_data["type"] = Type::partner_skill; 114 | json_data["monster_id"] = monster_id; 115 | json_data["skill_id"] = skill_id; 116 | 117 | return (send_data(json_data.dump()) != SOCKET_ERROR); 118 | } 119 | 120 | bool Phoenix::Api::pets_walk(int x, int y) 121 | { 122 | nlohmann::json json_data; 123 | json_data["type"] = Type::pets_walk; 124 | json_data["x"] = x; 125 | json_data["y"] = y; 126 | 127 | return (send_data(json_data.dump()) != SOCKET_ERROR); 128 | } 129 | 130 | bool Phoenix::Api::pick_up(int item_id) 131 | { 132 | nlohmann::json json_data; 133 | json_data["type"] = Type::pick_up; 134 | json_data["item_id"] = item_id; 135 | 136 | return (send_data(json_data.dump()) != SOCKET_ERROR); 137 | } 138 | 139 | bool Phoenix::Api::collect(int npc_id) 140 | { 141 | nlohmann::json json_data; 142 | json_data["type"] = Type::collect; 143 | json_data["npc_id"] = npc_id; 144 | 145 | return (send_data(json_data.dump()) != SOCKET_ERROR); 146 | } 147 | 148 | bool Phoenix::Api::start_bot() 149 | { 150 | nlohmann::json json_data; 151 | json_data["type"] = Type::start_bot; 152 | 153 | return (send_data(json_data.dump()) != SOCKET_ERROR); 154 | } 155 | 156 | bool Phoenix::Api::stop_bot() 157 | { 158 | nlohmann::json json_data; 159 | json_data["type"] = Type::stop_bot; 160 | 161 | return (send_data(json_data.dump()) != SOCKET_ERROR); 162 | } 163 | 164 | bool Phoenix::Api::continue_bot() 165 | { 166 | nlohmann::json json_data; 167 | json_data["type"] = Type::continue_bot; 168 | 169 | return (send_data(json_data.dump()) != SOCKET_ERROR); 170 | } 171 | 172 | bool Phoenix::Api::load_settings(const std::string& settings_path) 173 | { 174 | nlohmann::json json_data; 175 | json_data["type"] = Type::load_settings; 176 | json_data["path"] = settings_path; 177 | 178 | return (send_data(json_data.dump()) != SOCKET_ERROR); 179 | } 180 | 181 | bool Phoenix::Api::start_minigame_bot() 182 | { 183 | nlohmann::json json_data; 184 | json_data["type"] = Type::start_minigame_bot; 185 | 186 | return (send_data(json_data.dump()) != SOCKET_ERROR); 187 | } 188 | 189 | bool Phoenix::Api::stop_minigame_bot() 190 | { 191 | nlohmann::json json_data; 192 | json_data["type"] = Type::stop_minigame_bot; 193 | 194 | return (send_data(json_data.dump()) != SOCKET_ERROR); 195 | } 196 | 197 | bool Phoenix::Api::query_player_info() 198 | { 199 | nlohmann::json json_data; 200 | json_data["type"] = Type::query_player_info; 201 | 202 | return (send_data(json_data.dump()) != SOCKET_ERROR); 203 | } 204 | 205 | bool Phoenix::Api::query_inventory() 206 | { 207 | nlohmann::json json_data; 208 | json_data["type"] = Type::query_inventory; 209 | 210 | return (send_data(json_data.dump()) != SOCKET_ERROR); 211 | } 212 | 213 | bool Phoenix::Api::query_skills_info() 214 | { 215 | nlohmann::json json_data; 216 | json_data["type"] = Type::query_skills_info; 217 | 218 | return (send_data(json_data.dump()) != SOCKET_ERROR); 219 | } 220 | 221 | bool Phoenix::Api::query_map_entities() 222 | { 223 | nlohmann::json json_data; 224 | json_data["type"] = Type::query_map_entities; 225 | 226 | return (send_data(json_data.dump()) != SOCKET_ERROR); 227 | } 228 | 229 | bool Phoenix::Api::target_entity(int entity_id, int entity_type) 230 | { 231 | nlohmann::json json_data; 232 | json_data["type"] = Type::target_entity; 233 | json_data["entity_id"] = entity_id; 234 | json_data["entity_type"] = entity_type; 235 | 236 | return (send_data(json_data.dump()) != SOCKET_ERROR); 237 | } 238 | 239 | void Phoenix::Api::receive_messages() 240 | { 241 | constexpr int buffer_size = 4096; 242 | char buffer[buffer_size]; 243 | std::string data; 244 | size_t delim_pos; 245 | 246 | while (run) 247 | { 248 | memset(buffer, 0, buffer_size); 249 | 250 | if (recv(sock, buffer, buffer_size - 1, 0) <= 0) 251 | { 252 | std::cerr << "recv failed\n"; 253 | run = false; 254 | break; 255 | } 256 | 257 | data += buffer; 258 | 259 | while ((delim_pos = data.find('\1')) != std::string::npos) 260 | { 261 | std::string message = data.substr(0, delim_pos - 1); 262 | data.erase(0, delim_pos + 1); 263 | 264 | messages.push(message); 265 | } 266 | } 267 | } 268 | 269 | void Phoenix::Api::initialize_socket(int port) 270 | { 271 | if (instance_counter == 0) 272 | { 273 | WSADATA wsa_data; 274 | 275 | if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) 276 | { 277 | std::cerr << "WSAStartup failed\n"; 278 | exit(-1); 279 | } 280 | } 281 | 282 | sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 283 | 284 | if (sock == INVALID_SOCKET) 285 | { 286 | std::cerr << "socket failed\n"; 287 | exit(-1); 288 | } 289 | 290 | sockaddr_in addr; 291 | inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); 292 | addr.sin_family = AF_INET; 293 | addr.sin_port = htons(port); 294 | 295 | if (connect(sock, reinterpret_cast(&addr), sizeof(sockaddr_in)) != 0) 296 | { 297 | std::cerr << "connect failed\n"; 298 | closesocket(sock); 299 | WSACleanup(); 300 | exit(-1); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/PhoenixApi/Api.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Api.h 3 | * @brief Main file of the Phoenix Bot API 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include "Port_finder.h" 12 | #include "Safe_queue.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #pragma comment(lib, "ws2_32.lib") 20 | 21 | namespace Phoenix 22 | { 23 | /** 24 | * @brief Represents the type of messages that we can send/receive from the bot. 25 | */ 26 | enum class Type 27 | { 28 | packet_send, 29 | packet_recv, 30 | attack, 31 | player_skill, 32 | player_walk, 33 | pet_skill, 34 | partner_skill, 35 | pets_walk, 36 | pick_up, 37 | collect, 38 | start_bot, 39 | stop_bot, 40 | continue_bot, 41 | load_settings, 42 | start_minigame_bot, 43 | stop_minigame_bot, 44 | query_player_info, 45 | query_inventory, 46 | query_skills_info, 47 | query_map_entities, 48 | target_entity 49 | }; 50 | 51 | /** 52 | * @brief Class that handles the main logic of the API. 53 | * It connects to the given port and receive the messages from 54 | * the bot in a thread safe queue. 55 | */ 56 | class Api 57 | { 58 | public: 59 | /** 60 | * @brief Constructor. 61 | * @param port The port of the bot you want to connect 62 | */ 63 | explicit Api(int port); 64 | 65 | /** 66 | * @brief Destructor. 67 | */ 68 | ~Api(); 69 | 70 | /** 71 | * @brief Send data to the bot. 72 | * @param data The data to send, it must be a valid JSON string 73 | * @return If no error then returns the number of bytes sent, otherwise it returns SOCKET_ERROR 74 | */ 75 | int send_data(const std::string& data); 76 | 77 | /** 78 | * @brief Check if there is any message to read from the port. 79 | * @return true or false 80 | */ 81 | bool empty(); 82 | 83 | /** 84 | * @brief Get a message from the bot and pops it from the queue. 85 | * @return The first message in the queue. 86 | */ 87 | std::string get_message(); 88 | 89 | /** 90 | * @brief Wrapper to send a packet 91 | * @param packet The packet you want to send 92 | * @return false if there's any error, true otherwise 93 | */ 94 | bool send_packet(const std::string& packet); 95 | 96 | /** 97 | * @brief Wrapper to receive a packet 98 | * @param packet The packet you want to receive 99 | * @return false if there's any error, true otherwise 100 | */ 101 | bool recv_packet(const std::string& packet); 102 | 103 | /** 104 | * @brief Wrapper for attacking a monster with the skills set 105 | * in the bot 106 | * @param monster_id The id of the monster to attack 107 | * @return false if there's any error, true otherwise 108 | */ 109 | bool attack_monster(int monster_id); 110 | 111 | /** 112 | * @brief Wrapper for attacking a monster with a specific skill 113 | * @param monster_id The id of the monster to attack 114 | * @param skill_id The id of the skill to use 115 | * @return false if there's any error, true otherwise 116 | */ 117 | bool use_player_skill(int monster_id, int skill_id); 118 | 119 | /** 120 | * @brief Moves the player to a specific position 121 | * @return false if there's any error, true otherwise 122 | */ 123 | bool player_walk(int x, int y); 124 | 125 | /** 126 | * @brief Attack a monster with a specific pet skill 127 | * @param monster_id The id of the monster to attack 128 | * @param skill_id The id of the skill to use 129 | * @return false if there's any error, true otherwise 130 | */ 131 | bool use_pet_skill(int monster_id, int skill_id); 132 | 133 | /** 134 | * @brief Attack a monster with a specific partner skill 135 | * @param monster_id The id of the monster to attack 136 | * @param skill_id The id of the skill to use 137 | * @return false if there's any error, true otherwise 138 | */ 139 | bool use_partner_skill(int monster_id, int skill_id); 140 | 141 | /** 142 | * @brief Moves both the pet and partner to a specific position 143 | * @return false if there's any error, true otherwise 144 | */ 145 | bool pets_walk(int x, int y); 146 | 147 | /** 148 | * @brief Walk and pick up an item 149 | * @param item_id The id of the item to pick up 150 | * @return false if there's any error, true otherwise 151 | */ 152 | bool pick_up(int item_id); 153 | 154 | /** 155 | * @brief Collect an npc (like ice flowers, grass, etc) 156 | * @param npc_id The id of the npc to collect 157 | * @return false if there's any error, true otherwise 158 | */ 159 | bool collect(int npc_id); 160 | 161 | /** 162 | * @brief Start the farming bot 163 | */ 164 | bool start_bot(); 165 | 166 | /** 167 | * @brief Stop the farming bot 168 | */ 169 | bool stop_bot(); 170 | 171 | /** 172 | * @brief Continue the farming bot 173 | */ 174 | bool continue_bot(); 175 | 176 | /** 177 | * @brief Load a profile into the bot 178 | * @param settings_path The path to the .ini file 179 | * @return false if there's any error, true otherwise 180 | */ 181 | bool load_settings(const std::string& settings_path); 182 | 183 | /** 184 | * @brief Starts the minigame bot 185 | * @return false if there's any error, true otherwise 186 | */ 187 | bool start_minigame_bot(); 188 | 189 | /** 190 | * @brief Stops the minigame bot 191 | * @return false if there's any error, true otherwise 192 | */ 193 | bool stop_minigame_bot(); 194 | 195 | /** 196 | * @brief Query player information from the bot 197 | * @return false if there's any error, true otherwise 198 | */ 199 | bool query_player_info(); 200 | 201 | /** 202 | * @brief Query the inventory information from the bot 203 | * @return false if there's any error, true otherwise 204 | */ 205 | bool query_inventory(); 206 | 207 | /** 208 | * @brief Query player skills information from the bot 209 | * @return false if there's any error, true otherwise 210 | */ 211 | bool query_skills_info(); 212 | 213 | /** 214 | * @brief Query all the map entities from the bot 215 | * @return false if there's any error, true otherwise 216 | */ 217 | bool query_map_entities(); 218 | 219 | /** 220 | * @brief Target an entity client side. 221 | * @param entity_id The id of the entity that you want to target 222 | * @param entity_type The type of the entity that you want to target (1 - player, 2 - npc, 3 - monster) 223 | */ 224 | bool target_entity(int entity_id, int entity_type); 225 | 226 | private: 227 | /** 228 | * @brief Function that will be receiving all the messages 229 | * from the bot in a different thread. 230 | */ 231 | void receive_messages(); 232 | 233 | /** 234 | * @brief Initialize a socket connected to the given port 235 | * @param port The port to connect 236 | */ 237 | void initialize_socket(int port); 238 | 239 | static int instance_counter; 240 | SOCKET sock; 241 | Safe_queue messages; 242 | std::thread recv_thread; 243 | bool run; 244 | }; 245 | } 246 | 247 | 248 | 249 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/PhoenixApi/Port_finder.cpp: -------------------------------------------------------------------------------- 1 | #include "Port_finder.h" 2 | #include 3 | #include 4 | 5 | static std::vector ports; 6 | 7 | BOOL CALLBACK enum_windows_callback(HWND hwnd, LPARAM param) 8 | { 9 | const int title_size = GetWindowTextLengthA(hwnd); 10 | 11 | if (title_size <= 0) 12 | { 13 | return TRUE; 14 | } 15 | 16 | char* title = new char[title_size + 1]; 17 | GetWindowTextA(hwnd, title, title_size + 1); 18 | 19 | std::string title_wrapper(title); 20 | 21 | if (title_wrapper.find("] - Phoenix Bot:") == std::string::npos) 22 | { 23 | delete[] title; 24 | return TRUE; 25 | } 26 | 27 | std::string port = title_wrapper.substr(title_wrapper.find_last_of(':') + 1); 28 | 29 | if (port.empty()) 30 | { 31 | delete[] title; 32 | return TRUE; 33 | } 34 | 35 | ports.push_back(std::stoi(port)); 36 | 37 | delete[] title; 38 | return TRUE; 39 | } 40 | 41 | std::vector Phoenix::find_ports() 42 | { 43 | ports.clear(); 44 | 45 | EnumWindows(enum_windows_callback, 0); 46 | 47 | return ports; 48 | } 49 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/PhoenixApi/Port_finder.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Port_finder.h 3 | * @brief Additional API function to get the ports 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | namespace Phoenix 14 | { 15 | /** 16 | * @brief Find the ports from all the running bots 17 | * @return std::vector with the ports 18 | */ 19 | std::vector find_ports(); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/PhoenixApi/Safe_queue.cpp: -------------------------------------------------------------------------------- 1 | #include "Safe_queue.h" 2 | 3 | void Phoenix::Safe_queue::push(const std::string& message) 4 | { 5 | size_t length = message.size(); 6 | char* message_copy = new char[length + 1]; 7 | memcpy(message_copy, message.c_str(), length); 8 | message_copy[length] = '\0'; 9 | 10 | std::lock_guard lock(mutex); 11 | queue.push(message_copy); 12 | } 13 | 14 | void Phoenix::Safe_queue::pop() 15 | { 16 | std::lock_guard lock(mutex); 17 | char* message = queue.front(); 18 | queue.pop(); 19 | delete[] message; 20 | } 21 | 22 | std::string Phoenix::Safe_queue::front() 23 | { 24 | std::lock_guard lock(mutex); 25 | char* message = queue.front(); 26 | 27 | return std::string(message); 28 | } 29 | 30 | bool Phoenix::Safe_queue::empty() 31 | { 32 | std::lock_guard lock(mutex); 33 | return queue.empty(); 34 | } 35 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/PhoenixApi/Safe_queue.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Safe_queue.h 3 | * @brief Safe thread implementation of a std::queue 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace Phoenix 16 | { 17 | /** 18 | * @brief Thread safe char* queue 19 | */ 20 | class Safe_queue 21 | { 22 | public: 23 | void push(const std::string& message); 24 | 25 | void pop(); 26 | 27 | std::string front(); 28 | 29 | bool empty(); 30 | 31 | private: 32 | std::mutex mutex; 33 | std::queue queue; 34 | }; 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/README.md: -------------------------------------------------------------------------------- 1 | # Instant combat bot 2 | 3 | This bot is able to complete an instant combat and take the rewards of every round if you set it up correctly. 4 | 5 | ## Requirements 6 | 7 | * If you want to kill enemies set a high scan radius in the `Farming/Attack/Combat` tab and add the skills that you want to use in the `Farming/Attack/Player skills` tab. 8 | 9 | * If you want to heal your character set the items in the `Farming/Items` tab. 10 | 11 | * Additionally you can set up the `Farming/Loot` tab and the `Security` tab. 12 | 13 | * The instant combat bot settings are in a file named `ic_settings.ini`, there you can change the bot options (move to players, move to a point, the point coordinates, etc). 14 | 15 | ```ini 16 | # ic_settings.ini example 17 | [Settings] 18 | afk_last_round=true 19 | move_to_players=false 20 | move_to_point=true 21 | x=35 22 | y=64 23 | ``` 24 | 25 | ## How to use 26 | 27 | 1. Inject Phoenix Bot into the game. 28 | 29 | 2. Set up the bot taking into account the requirements listed above. 30 | 31 | 3. Open InstantCombat.exe 32 | 33 | 4. Select the index of the port that you want to use the bot on (You can find it in the bot title bar). 34 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Scene.cpp: -------------------------------------------------------------------------------- 1 | #include "Scene.h" 2 | 3 | void Scene::on_recv(const std::vector& packet_splitted, const std::string& full_packet) 4 | { 5 | std::string header = packet_splitted[0]; 6 | 7 | if (header == "in") 8 | handle_in(packet_splitted, full_packet); 9 | 10 | if (header == "mv") 11 | handle_mv(packet_splitted, full_packet); 12 | 13 | if (header == "c_map") 14 | handle_cmap(packet_splitted, full_packet); 15 | 16 | if (header == "su") 17 | handle_su(packet_splitted, full_packet); 18 | } 19 | 20 | std::pair Scene::get_central_player_position() 21 | { 22 | if (players.size() == 0) 23 | return { 0, 0 }; 24 | 25 | std::vector x_pos; 26 | std::vector y_pos; 27 | 28 | for (const auto& player : players) 29 | { 30 | x_pos.push_back(player.second.first); 31 | y_pos.push_back(player.second.second); 32 | } 33 | 34 | std::sort(x_pos.begin(), x_pos.end()); 35 | std::sort(y_pos.begin(), y_pos.end()); 36 | 37 | size_t index = players.size() / 2; 38 | 39 | return { x_pos[index], y_pos[index] }; 40 | } 41 | 42 | void Scene::handle_in(const std::vector& packet_splitted, const std::string& full_packet) 43 | { 44 | if (packet_splitted.size() < 10) 45 | return; 46 | 47 | if (packet_splitted[1] != "1") 48 | return; 49 | 50 | int id; 51 | int x; 52 | int y; 53 | 54 | try 55 | { 56 | id = std::stoi(packet_splitted[4]); 57 | x = std::stoi(packet_splitted[5]); 58 | y = std::stoi(packet_splitted[6]); 59 | 60 | players[id] = { x, y }; 61 | } 62 | catch (const std::exception& e) 63 | { 64 | std::cerr << "Scene::handle_in " << e.what() << std::endl; 65 | std::cerr << "Packet: " << full_packet << std::endl; 66 | } 67 | } 68 | 69 | void Scene::handle_mv(const std::vector& packet_splitted, const std::string& full_packet) 70 | { 71 | if (packet_splitted.size() < 6) 72 | return; 73 | 74 | if (packet_splitted[1] != "1") 75 | return; 76 | 77 | int id; 78 | int x; 79 | int y; 80 | 81 | try 82 | { 83 | id = std::stoi(packet_splitted[2]); 84 | x = std::stoi(packet_splitted[3]); 85 | y = std::stoi(packet_splitted[4]); 86 | 87 | players[id] = { x, y }; 88 | } 89 | catch (const std::exception& e) 90 | { 91 | std::cerr << "Scene::handle_mv " << e.what() << std::endl; 92 | std::cerr << "Packet: " << full_packet << std::endl; 93 | } 94 | } 95 | 96 | void Scene::handle_cmap(const std::vector& packet_splitted, const std::string& full_packet) 97 | { 98 | if (packet_splitted.size() < 4) 99 | return; 100 | 101 | players.clear(); 102 | } 103 | 104 | void Scene::handle_su(const std::vector& packet_splitted, const std::string& full_packet) 105 | { 106 | if (packet_splitted.size() < 13) 107 | return; 108 | 109 | int id; 110 | int type; 111 | int hp; 112 | 113 | try 114 | { 115 | id = std::stoi(packet_splitted[4]); 116 | type = std::stoi(packet_splitted[3]); 117 | hp = std::stoi(packet_splitted[12]); 118 | 119 | if (type != 1) 120 | return; 121 | 122 | if (hp == 0) 123 | players.erase(id); 124 | } 125 | 126 | catch (const std::exception& e) 127 | { 128 | std::cerr << "Bot::handle_su " << e.what() << std::endl; 129 | std::cerr << "Packet: " << full_packet << std::endl; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Module.h" 4 | #include 5 | #include 6 | #include 7 | 8 | class Scene : public Module 9 | { 10 | public: 11 | void on_recv(const std::vector& packet_splitted, const std::string& full_packet); 12 | std::pair get_central_player_position(); 13 | 14 | private: 15 | void handle_in(const std::vector& packet_splitted, const std::string& full_packet); 16 | void handle_mv(const std::vector& packet_splitted, const std::string& full_packet); 17 | void handle_cmap(const std::vector& packet_splitted, const std::string& full_packet); 18 | void handle_su(const std::vector& packet_splitted, const std::string& full_packet); 19 | 20 | std::unordered_map /* position */> players; 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Split_string.cpp: -------------------------------------------------------------------------------- 1 | #include "Split_string.h" 2 | 3 | std::vector split_string(const std::string& str, char delimiter) 4 | { 5 | std::vector splitted_string; 6 | 7 | size_t start = 0U; 8 | size_t end = str.find(delimiter); 9 | 10 | while (end != std::string::npos) 11 | { 12 | splitted_string.push_back(str.substr(start, end - start)); 13 | start = end + sizeof(char); 14 | end = str.find(delimiter, start); 15 | } 16 | 17 | splitted_string.push_back(str.substr(start, end)); 18 | 19 | return splitted_string; 20 | } 21 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/Split_string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | std::vector split_string(const std::string& str, char delimiter = ' '); 6 | 7 | -------------------------------------------------------------------------------- /cpp/examples/InstantCombat/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "PhoenixApi/Api.h" 5 | #include "Bot.h" 6 | #include "Scene.h" 7 | #include "Split_string.h" 8 | #include "Logger.h" 9 | #include 10 | 11 | int select_port(); 12 | 13 | class RunningBot { 14 | public: 15 | RunningBot(int port) : port(port) { 16 | scene = new Scene; 17 | api = new Phoenix::Api(port); 18 | bot = new Bot(api, scene); 19 | modules.push_back(scene); 20 | modules.push_back(bot); 21 | } 22 | 23 | ~RunningBot() { 24 | modules.clear(); 25 | delete bot; 26 | delete scene; 27 | delete api; 28 | } 29 | 30 | void update() { 31 | if (!api->empty()) 32 | { 33 | std::string message = api->get_message(); 34 | 35 | try 36 | { 37 | nlohmann::json json_msg = nlohmann::json::parse(message); 38 | 39 | if (json_msg["type"] == Phoenix::Type::packet_send) 40 | { 41 | std::string packet = json_msg["packet"]; 42 | std::vector packet_splitted = split_string(packet); 43 | 44 | if (packet_splitted.size() > 0) 45 | { 46 | for (auto mod : modules) 47 | mod->on_send(packet_splitted, packet); 48 | } 49 | } 50 | 51 | if (json_msg["type"] == Phoenix::Type::packet_recv) 52 | { 53 | std::string packet = json_msg["packet"]; 54 | std::vector packet_splitted = split_string(packet); 55 | 56 | if (packet_splitted.size() > 0) 57 | { 58 | for (auto mod : modules) 59 | mod->on_recv(packet_splitted, packet); 60 | } 61 | } 62 | 63 | if (json_msg["type"] == Phoenix::Type::query_map_entities) 64 | { 65 | bot->handle_map_entities(json_msg); 66 | } 67 | } 68 | catch (const std::exception& e) 69 | { 70 | std::stringstream ss; 71 | ss << "[" << port << "]: " << e.what() << std::endl; 72 | Logger::error(ss.str()); 73 | return; 74 | } 75 | } 76 | else 77 | { 78 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 79 | } 80 | 81 | bot->run(); 82 | } 83 | 84 | private: 85 | int port; 86 | Phoenix::Api* api; 87 | Bot* bot; 88 | Scene* scene; 89 | std::vector modules; 90 | }; 91 | 92 | void run_bot(int port) 93 | { 94 | Phoenix::Api api(port); 95 | Scene scene; 96 | Bot bot(&api, &scene); 97 | std::vector modules = { &scene, &bot }; 98 | 99 | std::stringstream ss; 100 | ss << "[" << port << "] Bot is running..." << std::endl; 101 | Logger::print(ss.str()); 102 | 103 | while (true) 104 | { 105 | if (!api.empty()) 106 | { 107 | std::string message = api.get_message(); 108 | 109 | try 110 | { 111 | nlohmann::json json_msg = nlohmann::json::parse(message); 112 | 113 | if (json_msg["type"] == Phoenix::Type::packet_send) 114 | { 115 | std::string packet = json_msg["packet"]; 116 | std::vector packet_splitted = split_string(packet); 117 | 118 | if (packet_splitted.size() > 0) 119 | { 120 | for (auto mod : modules) 121 | mod->on_send(packet_splitted, packet); 122 | } 123 | } 124 | 125 | if (json_msg["type"] == Phoenix::Type::packet_recv) 126 | { 127 | std::string packet = json_msg["packet"]; 128 | std::vector packet_splitted = split_string(packet); 129 | 130 | if (packet_splitted.size() > 0) 131 | { 132 | for (auto mod : modules) 133 | mod->on_recv(packet_splitted, packet); 134 | } 135 | } 136 | 137 | if (json_msg["type"] == Phoenix::Type::query_map_entities) 138 | { 139 | bot.handle_map_entities(json_msg); 140 | } 141 | } 142 | catch (const std::exception& e) 143 | { 144 | ss.clear(); 145 | ss << "[" << port << "]: " << e.what() << std::endl; 146 | Logger::error(ss.str()); 147 | return; 148 | } 149 | } 150 | else 151 | { 152 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 153 | } 154 | 155 | bot.run(); 156 | } 157 | } 158 | 159 | int main() 160 | { 161 | int selected_port = select_port(); 162 | 163 | if (selected_port == -1) 164 | return 0; 165 | 166 | if (selected_port == 0) 167 | { 168 | std::vector ports = Phoenix::find_ports(); 169 | std::vector bots; 170 | 171 | for (int port : ports) 172 | bots.push_back(std::thread(run_bot, port)); 173 | 174 | for (auto& bot : bots) 175 | if (bot.joinable()) bot.join(); 176 | } 177 | else 178 | { 179 | run_bot(selected_port); 180 | } 181 | 182 | return 0; 183 | } 184 | 185 | //int main() 186 | //{ 187 | // int selected_port = select_port(); 188 | // 189 | // if (selected_port == -1) 190 | // return 0; 191 | // 192 | // if (selected_port == 0) 193 | // { 194 | // std::vector ports = Phoenix::find_ports(); 195 | // std::vector bots; 196 | // 197 | // for (const int port : ports) { 198 | // RunningBot* bot = new RunningBot(port); 199 | // bots.push_back(bot); 200 | // } 201 | // 202 | // while (true) { 203 | // for (const auto& bot : bots) { 204 | // bot->update(); 205 | // } 206 | // } 207 | // 208 | // for (auto& bot : bots) { 209 | // delete bot; 210 | // } 211 | // } 212 | // else 213 | // { 214 | // run_bot(selected_port); 215 | // } 216 | // 217 | // return 0; 218 | //} 219 | 220 | int select_port() 221 | { 222 | int option = -1; 223 | int port = -1; 224 | 225 | std::vector ports = Phoenix::find_ports(); 226 | 227 | std::cout << "Select the port to connect (-1 to exit):" << std::endl; 228 | 229 | std::cout << "0) Select all" << std::endl; 230 | 231 | for (size_t i = 0; i < ports.size(); ++i) 232 | { 233 | std::cout << i + 1 << ") " << ports[i] << std::endl; 234 | } 235 | 236 | while (true) 237 | { 238 | std::cin >> option; 239 | 240 | if (option == -1) 241 | { 242 | std::cout << "Exiting..." << std::endl; 243 | break; 244 | } 245 | 246 | if (option == 0) 247 | { 248 | std::cout << "All ports selected" << std::endl; 249 | return 0; 250 | } 251 | 252 | else if (option < 0 || option > (int)ports.size()) 253 | { 254 | std::cout << "Selected option is not valid, try again." << std::endl; 255 | std::cout << "Available options are: "; 256 | 257 | std::cout << "0 "; 258 | for (size_t i = 0; i < ports.size(); ++i) 259 | { 260 | std::cout << i + 1 << " "; 261 | } 262 | 263 | std::cout << std::endl; 264 | } 265 | 266 | else 267 | { 268 | port = ports[option - 1]; 269 | break; 270 | } 271 | } 272 | 273 | return port; 274 | } 275 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Bot.cpp: -------------------------------------------------------------------------------- 1 | #include "Bot.h" 2 | 3 | Bot::Bot(Phoenix::Api* api, Player* player, Scene* scene) 4 | : map_change(0) 5 | , garg_spawn(0) 6 | , lever_pick(0) 7 | , last_lever_spawn(0) 8 | , api(api) 9 | , player(player) 10 | , scene(scene) 11 | , target_id(-1) 12 | , lever_id(-1) 13 | , failed(false) 14 | , map_changed(false) 15 | , run(true) 16 | { 17 | worker = std::thread(&Bot::work, this); 18 | } 19 | 20 | Bot::~Bot() 21 | { 22 | run = false; 23 | failed = true; 24 | 25 | if (worker.joinable()) 26 | worker.join(); 27 | } 28 | 29 | void Bot::on_send(const std::vector& packet_splitted, const std::string& full_packet) 30 | { 31 | std::string header = packet_splitted[0]; 32 | 33 | std::string test = header.substr(0, 4); 34 | 35 | if (test == "#git") 36 | handle_git(packet_splitted, full_packet); 37 | } 38 | 39 | void Bot::on_recv(const std::vector& packet_splitted, const std::string& full_packet) 40 | { 41 | std::string header = packet_splitted[0]; 42 | 43 | if (header == "in") 44 | handle_in(packet_splitted, full_packet); 45 | 46 | else if (header == "at") 47 | handle_at(packet_splitted, full_packet); 48 | 49 | else if (header == "su") 50 | handle_su(packet_splitted, full_packet); 51 | 52 | else if (header == "dlgi") 53 | handle_dlgi(packet_splitted, full_packet); 54 | 55 | else if (header == "npc_req") 56 | handle_npc_req(packet_splitted, full_packet); 57 | 58 | else if (header == "sayi") 59 | handle_sayi(packet_splitted, full_packet); 60 | 61 | else if (header == "score") 62 | handle_score(packet_splitted, full_packet); 63 | } 64 | 65 | void Bot::handle_in(const std::vector& packet_splitted, const std::string& full_packet) 66 | { 67 | if (packet_splitted.size() < 10) 68 | return; 69 | 70 | if (packet_splitted[1] == "1") 71 | return; 72 | 73 | int type; 74 | int vnum; 75 | int id; 76 | 77 | try 78 | { 79 | type = std::stoi(packet_splitted[1]); 80 | vnum = std::stoi(packet_splitted[2]); 81 | id = std::stoi(packet_splitted[3]); 82 | 83 | if (type == 3 && vnum == garg_vnum) 84 | { 85 | target_id = id; 86 | garg_spawn.release(); 87 | } 88 | 89 | if (type == 9 && (vnum == lever_vnum || vnum == crystal_vnum)) 90 | { 91 | lever_id = id; 92 | 93 | if (vnum == crystal_vnum) 94 | last_lever_spawn.release(); 95 | } 96 | } 97 | 98 | catch (const std::exception& e) 99 | { 100 | std::cerr << "Bot::handle_in " << e.what() << std::endl; 101 | std::cerr << "Packet: " << full_packet << std::endl; 102 | } 103 | } 104 | 105 | void Bot::handle_at(const std::vector& packet_splitted, const std::string& full_packet) 106 | { 107 | target_id = -1; 108 | lever_id = -1; 109 | map_changed = true; 110 | 111 | map_change.release(); 112 | } 113 | 114 | void Bot::handle_su(const std::vector& packet_splitted, const std::string& full_packet) 115 | { 116 | if (packet_splitted.size() < 13) 117 | return; 118 | 119 | int id; 120 | int type; 121 | int hp; 122 | 123 | try 124 | { 125 | id = std::stoi(packet_splitted[4]); 126 | type = std::stoi(packet_splitted[3]); 127 | hp = std::stoi(packet_splitted[12]); 128 | 129 | if (type == 3 && hp == 0 && id == target_id) 130 | target_id = -1; 131 | } 132 | 133 | catch (const std::exception& e) 134 | { 135 | std::cerr << "Bot::handle_su " << e.what() << std::endl; 136 | std::cerr << "Packet: " << full_packet << std::endl; 137 | } 138 | } 139 | 140 | void Bot::handle_dlgi(const std::vector& packet_splitted, const std::string& full_packet) 141 | { 142 | if (packet_splitted.size() < 6) 143 | return; 144 | 145 | std::string accept = packet_splitted[1]; 146 | 147 | auto pos = accept.find('^'); 148 | 149 | if (accept.substr(0, pos) == "#rstart") 150 | api->send_packet(accept); 151 | } 152 | 153 | void Bot::handle_git(const std::vector& packet_splitted, const std::string& full_packet) 154 | { 155 | if (packet_splitted.size() > 1) 156 | return; 157 | 158 | auto pos = packet_splitted[0].find('^'); 159 | 160 | int id; 161 | 162 | try 163 | { 164 | id = std::stoi(packet_splitted[0].substr(pos + 1)); 165 | 166 | if (id == lever_id) 167 | { 168 | lever_id = -1; 169 | lever_pick.release(); 170 | } 171 | } 172 | 173 | catch (const std::exception& e) 174 | { 175 | std::cerr << "Bot::handle_git " << e.what() << std::endl; 176 | std::cerr << "Packet: " << full_packet << std::endl; 177 | } 178 | } 179 | 180 | void Bot::handle_npc_req(const std::vector& packet_splitted, const std::string& full_packet) 181 | { 182 | if (packet_splitted.size() < 4) 183 | return; 184 | 185 | int player_id; 186 | int id; 187 | 188 | try 189 | { 190 | player_id = std::stoi(packet_splitted[2]); 191 | id = std::stoi(packet_splitted[3]); 192 | 193 | if (player_id != player->get_id()) 194 | return; 195 | 196 | if (id == 6078 || id == 6081 || id == 6082) 197 | api->send_packet("n_run 5 0 " + std::to_string(player_id)); 198 | else if (id == 6079) 199 | api->send_packet("n_run 6 0 1 " + std::to_string(player_id)); 200 | } 201 | 202 | catch (const std::exception& e) 203 | { 204 | std::cerr << "Bot::handle_npc_req " << e.what() << std::endl; 205 | std::cerr << "Packet: " << full_packet << std::endl; 206 | } 207 | } 208 | 209 | void Bot::handle_sayi(const std::vector& packet_splitted, const std::string& full_packet) 210 | { 211 | if (packet_splitted.size() < 10) 212 | return; 213 | 214 | int entity_id; 215 | 216 | try 217 | { 218 | entity_id = std::stoi(packet_splitted[2]); 219 | 220 | if (entity_id != player->get_id() || packet_splitted[3] != "10" || packet_splitted[4] != "94") 221 | return; 222 | 223 | api->send_packet("preq"); 224 | } 225 | 226 | catch (const std::exception& e) 227 | { 228 | std::cerr << "Bot::handle_sayi " << e.what() << std::endl; 229 | std::cerr << "Packet: " << full_packet << std::endl; 230 | } 231 | } 232 | 233 | void Bot::handle_score(const std::vector& packet_splitted, const std::string& full_packet) 234 | { 235 | if (packet_splitted.size() < 11) 236 | return; 237 | 238 | if (full_packet == "score 1 0 0 0 0 0 0 0 0 0" || full_packet == "score 3 0 0 0 0 0 0 0 0 0") 239 | { 240 | failed = true; 241 | map_change.release(); 242 | garg_spawn.release(); 243 | lever_pick.release(); 244 | last_lever_spawn.release(); 245 | 246 | api->send_packet("escape"); 247 | 248 | std::cout << "Time-space failed" << std::endl; 249 | } 250 | } 251 | 252 | void Bot::work() 253 | { 254 | while (run) 255 | { 256 | while (player->get_map_id() == -1 || player->get_map_id() == sunny_meadows_id) 257 | { 258 | api->send_packet("wreq"); 259 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 260 | api->send_packet("wreq 1"); 261 | std::this_thread::sleep_for(std::chrono::milliseconds(5000)); 262 | } 263 | 264 | if (player->get_map_id() == first_ts_room_id) 265 | { 266 | failed = false; 267 | 268 | map_change.try_acquire(); 269 | garg_spawn.try_acquire(); 270 | lever_pick.try_acquire(); 271 | last_lever_spawn.try_acquire(); 272 | walk(left_portal.first, left_portal.second); 273 | wait_map_change(); 274 | walk(left_portal.first, left_portal.second); 275 | wait_map_change(); 276 | complete_room(); 277 | walk(left_portal.first, left_portal.second); 278 | wait_map_change(); 279 | complete_room(); 280 | walk(bottom_portal.first, bottom_portal.second); 281 | wait_map_change(); 282 | complete_room(); 283 | walk(right_portal.first, right_portal.second); 284 | wait_map_change(); 285 | complete_room(); 286 | walk(bottom_portal.first, bottom_portal.second); 287 | wait_map_change(); 288 | complete_room(); 289 | walk(right_portal.first, right_portal.second); 290 | wait_map_change(); 291 | 292 | garg_spawn.try_acquire(); 293 | 294 | if (!failed) 295 | last_lever_spawn.acquire(); 296 | 297 | api->pick_up(lever_id); 298 | 299 | if (!failed) 300 | { 301 | lever_pick.acquire(); 302 | garg_spawn.acquire(); 303 | } 304 | 305 | attack_garg(); 306 | 307 | if (!failed) 308 | std::this_thread::sleep_for(std::chrono::milliseconds(2000)); 309 | 310 | pick_up_items(); 311 | 312 | if (!failed) 313 | { 314 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 315 | api->send_packet("escape"); 316 | map_change.acquire(); 317 | } 318 | } 319 | } 320 | } 321 | 322 | void Bot::complete_room() 323 | { 324 | if (!failed) 325 | { 326 | garg_spawn.acquire(); 327 | attack_garg(); 328 | if (!failed) 329 | std::this_thread::sleep_for(std::chrono::milliseconds(2000)); 330 | 331 | api->pick_up(lever_id); 332 | 333 | if (!failed) 334 | { 335 | lever_pick.acquire(); 336 | std::this_thread::sleep_for(std::chrono::milliseconds(2000)); 337 | } 338 | 339 | pick_up_items(); 340 | } 341 | } 342 | 343 | void Bot::pick_up_items() 344 | { 345 | const clock_t start = clock(); 346 | 347 | while (scene->get_items_count() > 0 && !failed) 348 | { 349 | auto items = scene->get_items(); 350 | 351 | int id = 0; 352 | 353 | for (const auto item : items) 354 | { 355 | id = item; 356 | break; 357 | } 358 | 359 | if (id == 0) 360 | break; 361 | 362 | else 363 | { 364 | api->pick_up(id); 365 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 366 | } 367 | 368 | float diff = float(clock() - start) / CLOCKS_PER_SEC; 369 | 370 | if (diff > 20.0f) 371 | { 372 | std::cout << "Couldn't pick up the items in 20 seconds" << std::endl; 373 | break; 374 | } 375 | } 376 | } 377 | 378 | void Bot::attack_garg() 379 | { 380 | while (target_id > 0 && !failed) 381 | { 382 | api->attack_monster(target_id); 383 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 384 | } 385 | } 386 | 387 | void Bot::wait_map_change() 388 | { 389 | if (!failed) 390 | map_change.acquire(); 391 | } 392 | 393 | void Bot::walk(int x, int y) 394 | { 395 | map_changed = false; 396 | while ((player->get_x() != x || player->get_y() != y) && !failed && !map_changed) 397 | { 398 | api->player_walk(x, y); 399 | api->pets_walk(x, y); 400 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Bot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Module.h" 4 | #include "Player.h" 5 | #include "Scene.h" 6 | #include "PhoenixApi/Api.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class Bot : public Module 14 | { 15 | public: 16 | explicit Bot(Phoenix::Api* api, Player* player, Scene* scene); 17 | ~Bot(); 18 | 19 | void on_send(const std::vector& packet_splitted, const std::string& full_packet) override; 20 | void on_recv(const std::vector& packet_splitted, const std::string& full_packet) override; 21 | 22 | private: 23 | void handle_in(const std::vector& packet_splitted, const std::string& full_packet); 24 | void handle_at(const std::vector& packet_splitted, const std::string& full_packet); 25 | void handle_su(const std::vector& packet_splitted, const std::string& full_packet); 26 | void handle_dlgi(const std::vector& packet_splitted, const std::string& full_packet); 27 | void handle_git(const std::vector& packet_splitted, const std::string& full_packet); 28 | void handle_npc_req(const std::vector& packet_splitted, const std::string& full_packet); 29 | void handle_sayi(const std::vector& packet_splitted, const std::string& full_packet); 30 | void handle_score(const std::vector& packet_splitted, const std::string& full_packet); 31 | void work(); 32 | 33 | void complete_room(); 34 | void pick_up_items(); 35 | void attack_garg(); 36 | void wait_map_change(); 37 | void walk(int x, int y); 38 | 39 | static constexpr int garg_vnum = 271; 40 | static constexpr int lever_vnum = 1051; 41 | static constexpr int crystal_vnum = 1048; 42 | static constexpr int sunny_meadows_id = 4; 43 | static constexpr int first_ts_room_id = 4204; 44 | static constexpr std::pair left_portal = { 1, 15 }; 45 | static constexpr std::pair right_portal = { 28, 15 }; 46 | static constexpr std::pair top_portal = { 14, 1 }; 47 | static constexpr std::pair bottom_portal = { 14, 28 }; 48 | 49 | std::binary_semaphore map_change; 50 | std::binary_semaphore garg_spawn; 51 | std::binary_semaphore lever_pick; 52 | std::binary_semaphore last_lever_spawn; 53 | std::thread worker; 54 | Phoenix::Api* api; 55 | Player* player; 56 | Scene* scene; 57 | std::atomic target_id; 58 | std::atomic lever_id; 59 | std::atomic failed; 60 | std::atomic map_changed; 61 | bool run; 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "PhoenixApi/Api.h" 4 | 5 | class Module 6 | { 7 | public: 8 | virtual void on_send(const std::vector& packet_splitted, const std::string& full_packet) {} 9 | virtual void on_recv(const std::vector& packet_splitted, const std::string& full_packet) {} 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/PhoenixApi/Api.cpp: -------------------------------------------------------------------------------- 1 | #include "Api.h" 2 | 3 | int Phoenix::Api::instance_counter = 0; 4 | 5 | Phoenix::Api::Api(int port) : run(true) 6 | { 7 | initialize_socket(port); 8 | ++instance_counter; 9 | recv_thread = std::thread(&Api::receive_messages, this); 10 | } 11 | 12 | Phoenix::Api::~Api() 13 | { 14 | if (recv_thread.joinable()) 15 | { 16 | run = false; 17 | recv_thread.join(); 18 | } 19 | 20 | --instance_counter; 21 | closesocket(sock); 22 | 23 | if (instance_counter == 0) 24 | { 25 | WSACleanup(); 26 | } 27 | } 28 | 29 | int Phoenix::Api::send_data(const std::string& data) 30 | { 31 | std::string msg = data + '\1'; 32 | return send(sock, msg.c_str(), msg.size(), 0); 33 | } 34 | 35 | bool Phoenix::Api::empty() 36 | { 37 | return messages.empty(); 38 | } 39 | 40 | std::string Phoenix::Api::get_message() 41 | { 42 | if (messages.empty()) 43 | { 44 | return std::string(); 45 | } 46 | 47 | std::string msg = messages.front(); 48 | messages.pop(); 49 | 50 | return msg; 51 | } 52 | 53 | bool Phoenix::Api::send_packet(const std::string& packet) 54 | { 55 | nlohmann::json json_data; 56 | json_data["type"] = Type::packet_send; 57 | json_data["packet"] = packet; 58 | 59 | return (send_data(json_data.dump()) != SOCKET_ERROR); 60 | } 61 | 62 | bool Phoenix::Api::recv_packet(const std::string& packet) 63 | { 64 | nlohmann::json json_data; 65 | json_data["type"] = Type::packet_recv; 66 | json_data["packet"] = packet; 67 | 68 | return (send_data(json_data.dump()) != SOCKET_ERROR); 69 | } 70 | 71 | bool Phoenix::Api::attack_monster(int monster_id) 72 | { 73 | nlohmann::json json_data; 74 | json_data["type"] = Type::attack; 75 | json_data["monster_id"] = monster_id; 76 | 77 | return (send_data(json_data.dump()) != SOCKET_ERROR); 78 | } 79 | 80 | bool Phoenix::Api::use_player_skill(int monster_id, int skill_id) 81 | { 82 | nlohmann::json json_data; 83 | json_data["type"] = Type::player_skill; 84 | json_data["monster_id"] = monster_id; 85 | json_data["skill_id"] = skill_id; 86 | 87 | return (send_data(json_data.dump()) != SOCKET_ERROR); 88 | } 89 | 90 | bool Phoenix::Api::player_walk(int x, int y) 91 | { 92 | nlohmann::json json_data; 93 | json_data["type"] = Type::player_walk; 94 | json_data["x"] = x; 95 | json_data["y"] = y; 96 | 97 | return (send_data(json_data.dump()) != SOCKET_ERROR); 98 | } 99 | 100 | bool Phoenix::Api::use_pet_skill(int monster_id, int skill_id) 101 | { 102 | nlohmann::json json_data; 103 | json_data["type"] = Type::pet_skill; 104 | json_data["monster_id"] = monster_id; 105 | json_data["skill_id"] = skill_id; 106 | 107 | return (send_data(json_data.dump()) != SOCKET_ERROR); 108 | } 109 | 110 | bool Phoenix::Api::use_partner_skill(int monster_id, int skill_id) 111 | { 112 | nlohmann::json json_data; 113 | json_data["type"] = Type::partner_skill; 114 | json_data["monster_id"] = monster_id; 115 | json_data["skill_id"] = skill_id; 116 | 117 | return (send_data(json_data.dump()) != SOCKET_ERROR); 118 | } 119 | 120 | bool Phoenix::Api::pets_walk(int x, int y) 121 | { 122 | nlohmann::json json_data; 123 | json_data["type"] = Type::pets_walk; 124 | json_data["x"] = x; 125 | json_data["y"] = y; 126 | 127 | return (send_data(json_data.dump()) != SOCKET_ERROR); 128 | } 129 | 130 | bool Phoenix::Api::pick_up(int item_id) 131 | { 132 | nlohmann::json json_data; 133 | json_data["type"] = Type::pick_up; 134 | json_data["item_id"] = item_id; 135 | 136 | return (send_data(json_data.dump()) != SOCKET_ERROR); 137 | } 138 | 139 | bool Phoenix::Api::collect(int npc_id) 140 | { 141 | nlohmann::json json_data; 142 | json_data["type"] = Type::collect; 143 | json_data["npc_id"] = npc_id; 144 | 145 | return (send_data(json_data.dump()) != SOCKET_ERROR); 146 | } 147 | 148 | bool Phoenix::Api::start_bot() 149 | { 150 | nlohmann::json json_data; 151 | json_data["type"] = Type::start_bot; 152 | 153 | return (send_data(json_data.dump()) != SOCKET_ERROR); 154 | } 155 | 156 | bool Phoenix::Api::stop_bot() 157 | { 158 | nlohmann::json json_data; 159 | json_data["type"] = Type::stop_bot; 160 | 161 | return (send_data(json_data.dump()) != SOCKET_ERROR); 162 | } 163 | 164 | bool Phoenix::Api::continue_bot() 165 | { 166 | nlohmann::json json_data; 167 | json_data["type"] = Type::continue_bot; 168 | 169 | return (send_data(json_data.dump()) != SOCKET_ERROR); 170 | } 171 | 172 | bool Phoenix::Api::load_settings(const std::string& settings_path) 173 | { 174 | nlohmann::json json_data; 175 | json_data["type"] = Type::load_settings; 176 | json_data["path"] = settings_path; 177 | 178 | return (send_data(json_data.dump()) != SOCKET_ERROR); 179 | } 180 | 181 | bool Phoenix::Api::start_minigame_bot() 182 | { 183 | nlohmann::json json_data; 184 | json_data["type"] = Type::start_minigame_bot; 185 | 186 | return (send_data(json_data.dump()) != SOCKET_ERROR); 187 | } 188 | 189 | bool Phoenix::Api::stop_minigame_bot() 190 | { 191 | nlohmann::json json_data; 192 | json_data["type"] = Type::stop_minigame_bot; 193 | 194 | return (send_data(json_data.dump()) != SOCKET_ERROR); 195 | } 196 | 197 | void Phoenix::Api::receive_messages() 198 | { 199 | constexpr int buffer_size = 4096; 200 | char buffer[buffer_size]; 201 | std::string data; 202 | size_t delim_pos; 203 | 204 | while (run) 205 | { 206 | memset(buffer, 0, buffer_size); 207 | 208 | if (recv(sock, buffer, buffer_size - 1, 0) <= 0) 209 | { 210 | std::cerr << "recv failed\n"; 211 | run = false; 212 | break; 213 | } 214 | 215 | data += buffer; 216 | 217 | while ((delim_pos = data.find('\1')) != std::string::npos) 218 | { 219 | std::string message = data.substr(0, delim_pos - 1); 220 | data.erase(0, delim_pos + 1); 221 | 222 | messages.push(message); 223 | } 224 | } 225 | } 226 | 227 | void Phoenix::Api::initialize_socket(int port) 228 | { 229 | if (instance_counter == 0) 230 | { 231 | WSADATA wsa_data; 232 | 233 | if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) 234 | { 235 | std::cerr << "WSAStartup failed\n"; 236 | exit(-1); 237 | } 238 | } 239 | 240 | sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 241 | 242 | if (sock == INVALID_SOCKET) 243 | { 244 | std::cerr << "socket failed\n"; 245 | exit(-1); 246 | } 247 | 248 | sockaddr_in addr; 249 | inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); 250 | addr.sin_family = AF_INET; 251 | addr.sin_port = htons(port); 252 | 253 | if (connect(sock, reinterpret_cast(&addr), sizeof(sockaddr_in)) != 0) 254 | { 255 | std::cerr << "connect failed\n"; 256 | closesocket(sock); 257 | WSACleanup(); 258 | exit(-1); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/PhoenixApi/Api.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Api.h 3 | * @brief Main file of the Phoenix Bot API 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include "Port_finder.h" 12 | #include "Safe_queue.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #pragma comment(lib, "ws2_32.lib") 20 | 21 | namespace Phoenix 22 | { 23 | /** 24 | * @brief Represents the type of messages that we can send/receive from the bot. 25 | */ 26 | enum class Type 27 | { 28 | packet_send, 29 | packet_recv, 30 | attack, 31 | player_skill, 32 | player_walk, 33 | pet_skill, 34 | partner_skill, 35 | pets_walk, 36 | pick_up, 37 | collect, 38 | start_bot, 39 | stop_bot, 40 | continue_bot, 41 | load_settings, 42 | start_minigame_bot, 43 | stop_minigame_bot 44 | }; 45 | 46 | /** 47 | * @brief Class that handles the main logic of the API. 48 | * It connects to the given port and receive the messages from 49 | * the bot in a thread safe queue. 50 | */ 51 | class Api 52 | { 53 | public: 54 | /** 55 | * @brief Constructor. 56 | * @param port The port of the bot you want to connect 57 | */ 58 | explicit Api(int port); 59 | 60 | /** 61 | * @brief Destructor. 62 | */ 63 | ~Api(); 64 | 65 | /** 66 | * @brief Send data to the bot. 67 | * @param data The data to send, it must be a valid JSON string 68 | * @return If no error then returns the number of bytes sent, otherwise it returns SOCKET_ERROR 69 | */ 70 | int send_data(const std::string& data); 71 | 72 | /** 73 | * @brief Check if there is any message to read from the port. 74 | * @return true or false 75 | */ 76 | bool empty(); 77 | 78 | /** 79 | * @brief Get a message from the bot and pops it from the queue. 80 | * @return The first message in the queue. 81 | */ 82 | std::string get_message(); 83 | 84 | /** 85 | * @brief Wrapper to send a packet 86 | * @param packet The packet you want to send 87 | * @return false if there's any error, true otherwise 88 | */ 89 | bool send_packet(const std::string& packet); 90 | 91 | /** 92 | * @brief Wrapper to receive a packet 93 | * @param packet The packet you want to receive 94 | * @return false if there's any error, true otherwise 95 | */ 96 | bool recv_packet(const std::string& packet); 97 | 98 | /** 99 | * @brief Wrapper for attacking a monster with the skills set 100 | * in the bot 101 | * @param monster_id The id of the monster to attack 102 | * @return false if there's any error, true otherwise 103 | */ 104 | bool attack_monster(int monster_id); 105 | 106 | /** 107 | * @brief Wrapper for attacking a monster with a specific skill 108 | * @param monster_id The id of the monster to attack 109 | * @param skill_id The id of the skill to use 110 | * @return false if there's any error, true otherwise 111 | */ 112 | bool use_player_skill(int monster_id, int skill_id); 113 | 114 | /** 115 | * @brief Moves the player to a specific position 116 | * @return false if there's any error, true otherwise 117 | */ 118 | bool player_walk(int x, int y); 119 | 120 | /** 121 | * @brief Attack a monster with a specific pet skill 122 | * @param monster_id The id of the monster to attack 123 | * @param skill_id The id of the skill to use 124 | * @return false if there's any error, true otherwise 125 | */ 126 | bool use_pet_skill(int monster_id, int skill_id); 127 | 128 | /** 129 | * @brief Attack a monster with a specific partner skill 130 | * @param monster_id The id of the monster to attack 131 | * @param skill_id The id of the skill to use 132 | * @return false if there's any error, true otherwise 133 | */ 134 | bool use_partner_skill(int monster_id, int skill_id); 135 | 136 | /** 137 | * @brief Moves both the pet and partner to a specific position 138 | * @return false if there's any error, true otherwise 139 | */ 140 | bool pets_walk(int x, int y); 141 | 142 | /** 143 | * @brief Walk and pick up an item 144 | * @param item_id The id of the item to pick up 145 | * @return false if there's any error, true otherwise 146 | */ 147 | bool pick_up(int item_id); 148 | 149 | /** 150 | * @brief Collect an npc (like ice flowers, grass, etc) 151 | * @param npc_id The id of the npc to collect 152 | * @return false if there's any error, true otherwise 153 | */ 154 | bool collect(int npc_id); 155 | 156 | /** 157 | * @brief Start the farming bot 158 | */ 159 | bool start_bot(); 160 | 161 | /** 162 | * @brief Stop the farming bot 163 | */ 164 | bool stop_bot(); 165 | 166 | /** 167 | * @brief Continue the farming bot 168 | */ 169 | bool continue_bot(); 170 | 171 | /** 172 | * @brief Load a profile into the bot 173 | * @param settings_path The path to the .ini file 174 | * @return false if there's any error, true otherwise 175 | */ 176 | bool load_settings(const std::string& settings_path); 177 | 178 | /** 179 | * @brief Starts the minigame bot 180 | * @return false if there's any error, true otherwise 181 | */ 182 | bool start_minigame_bot(); 183 | 184 | /** 185 | * @brief Stops the minigame bot 186 | * @return false if there's any error, true otherwise 187 | */ 188 | bool stop_minigame_bot(); 189 | 190 | private: 191 | /** 192 | * @brief Function that will be receiving all the messages 193 | * from the bot in a different thread. 194 | */ 195 | void receive_messages(); 196 | 197 | /** 198 | * @brief Initialize a socket connected to the given port 199 | * @param port The port to connect 200 | */ 201 | void initialize_socket(int port); 202 | 203 | static int instance_counter; 204 | SOCKET sock; 205 | Safe_queue messages; 206 | std::thread recv_thread; 207 | bool run; 208 | }; 209 | } 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/PhoenixApi/Port_finder.cpp: -------------------------------------------------------------------------------- 1 | #include "Port_finder.h" 2 | #include 3 | #include 4 | 5 | static std::vector ports; 6 | 7 | BOOL CALLBACK enum_windows_callback(HWND hwnd, LPARAM param) 8 | { 9 | const int title_size = GetWindowTextLengthA(hwnd); 10 | 11 | if (title_size <= 0) 12 | { 13 | return TRUE; 14 | } 15 | 16 | char* title = new char[title_size + 1]; 17 | GetWindowTextA(hwnd, title, title_size + 1); 18 | 19 | std::string title_wrapper(title); 20 | 21 | if (title_wrapper.find("] - Phoenix Bot:") == std::string::npos) 22 | { 23 | delete[] title; 24 | return TRUE; 25 | } 26 | 27 | std::string port = title_wrapper.substr(title_wrapper.find_last_of(':') + 1); 28 | 29 | if (port.empty()) 30 | { 31 | delete[] title; 32 | return TRUE; 33 | } 34 | 35 | ports.push_back(std::stoi(port)); 36 | 37 | delete[] title; 38 | return TRUE; 39 | } 40 | 41 | std::vector Phoenix::find_ports() 42 | { 43 | ports.clear(); 44 | 45 | EnumWindows(enum_windows_callback, 0); 46 | 47 | return ports; 48 | } 49 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/PhoenixApi/Port_finder.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Port_finder.h 3 | * @brief Additional API function to get the ports 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | namespace Phoenix 14 | { 15 | /** 16 | * @brief Find the ports from all the running bots 17 | * @return std::vector with the ports 18 | */ 19 | std::vector find_ports(); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/PhoenixApi/Safe_queue.cpp: -------------------------------------------------------------------------------- 1 | #include "Safe_queue.h" 2 | 3 | void Phoenix::Safe_queue::push(const std::string& message) 4 | { 5 | size_t length = message.size(); 6 | char* message_copy = new char[length + 1]; 7 | memcpy(message_copy, message.c_str(), length); 8 | message_copy[length] = '\0'; 9 | 10 | std::lock_guard lock(mutex); 11 | queue.push(message_copy); 12 | } 13 | 14 | void Phoenix::Safe_queue::pop() 15 | { 16 | std::lock_guard lock(mutex); 17 | char* message = queue.front(); 18 | queue.pop(); 19 | delete[] message; 20 | } 21 | 22 | std::string Phoenix::Safe_queue::front() 23 | { 24 | std::lock_guard lock(mutex); 25 | char* message = queue.front(); 26 | 27 | return std::string(message); 28 | } 29 | 30 | bool Phoenix::Safe_queue::empty() 31 | { 32 | std::lock_guard lock(mutex); 33 | return queue.empty(); 34 | } 35 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/PhoenixApi/Safe_queue.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * @file Safe_queue.h 3 | * @brief Safe thread implementation of a std::queue 4 | * 5 | * @author Hatz 6 | * @date December 2021 7 | *********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace Phoenix 16 | { 17 | /** 18 | * @brief Thread safe char* queue 19 | */ 20 | class Safe_queue 21 | { 22 | public: 23 | void push(const std::string& message); 24 | 25 | void pop(); 26 | 27 | std::string front(); 28 | 29 | bool empty(); 30 | 31 | private: 32 | std::mutex mutex; 33 | std::queue queue; 34 | }; 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Player.cpp: -------------------------------------------------------------------------------- 1 | #include "Player.h" 2 | 3 | Player::Player() 4 | : x(-1) 5 | , y(-1) 6 | , id(-1) 7 | , map_id(-1) 8 | { 9 | } 10 | 11 | void Player::on_send(const std::vector& packet_splitted, const std::string& full_packet) 12 | { 13 | std::string header = packet_splitted[0]; 14 | 15 | if (header == "walk") 16 | handle_walk(packet_splitted, full_packet); 17 | } 18 | 19 | void Player::on_recv(const std::vector& packet_splitted, const std::string& full_packet) 20 | { 21 | std::string header = packet_splitted[0]; 22 | 23 | if (header == "at") 24 | handle_at(packet_splitted, full_packet); 25 | 26 | else if (header == "c_info") 27 | handle_cinfo(packet_splitted, full_packet); 28 | } 29 | 30 | int Player::get_x() 31 | { 32 | std::lock_guard lock(mtx); 33 | return x; 34 | } 35 | 36 | int Player::get_y() 37 | { 38 | std::lock_guard lock(mtx); 39 | return y; 40 | } 41 | 42 | int Player::get_id() 43 | { 44 | std::lock_guard lock(mtx); 45 | return id; 46 | } 47 | 48 | int Player::get_map_id() 49 | { 50 | std::lock_guard lock(mtx); 51 | return map_id; 52 | } 53 | 54 | void Player::handle_at(const std::vector& packet_splitted, const std::string& full_packet) 55 | { 56 | if (packet_splitted.size() < 9) 57 | return; 58 | 59 | std::lock_guard lock(mtx); 60 | 61 | try 62 | { 63 | id = std::stoi(packet_splitted[1]); 64 | map_id = std::stoi(packet_splitted[2]); 65 | x = std::stoi(packet_splitted[3]); 66 | y = std::stoi(packet_splitted[4]); 67 | } 68 | 69 | catch (const std::exception& e) 70 | { 71 | std::cerr << "Player::handle_at " << e.what() << std::endl; 72 | std::cerr << "Packet: " << full_packet << std::endl; 73 | } 74 | } 75 | 76 | void Player::handle_walk(const std::vector& packet_splitted, const std::string& full_packet) 77 | { 78 | if (packet_splitted.size() < 5) 79 | return; 80 | 81 | std::lock_guard lock(mtx); 82 | 83 | try 84 | { 85 | x = std::stoi(packet_splitted[1]); 86 | y = std::stoi(packet_splitted[2]); 87 | } 88 | 89 | catch (const std::exception& e) 90 | { 91 | std::cerr << "Player::handle_walk " << e.what() << std::endl; 92 | std::cerr << "Packet: " << full_packet << std::endl; 93 | } 94 | } 95 | 96 | void Player::handle_cinfo(const std::vector& packet_splitted, const std::string& full_packet) 97 | { 98 | if (packet_splitted.size() < 20) 99 | return; 100 | 101 | std::lock_guard lock(mtx); 102 | 103 | try 104 | { 105 | id = std::stoi(packet_splitted[6]); 106 | } 107 | 108 | catch (const std::exception& e) 109 | { 110 | std::cerr << "Player::handle_cinfo " << e.what() << std::endl; 111 | std::cerr << "Packet: " << full_packet << std::endl; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Player.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Module.h" 4 | #include 5 | 6 | class Player : public Module 7 | { 8 | public: 9 | Player(); 10 | 11 | void on_send(const std::vector& packet_splitted, const std::string& full_packet) override; 12 | void on_recv(const std::vector& packet_splitted, const std::string& full_packet) override; 13 | 14 | int get_x(); 15 | int get_y(); 16 | int get_id(); 17 | int get_map_id(); 18 | 19 | private: 20 | void handle_at(const std::vector& packet_splitted, const std::string& full_packet); 21 | void handle_walk(const std::vector& packet_splitted, const std::string& full_packet); 22 | void handle_cinfo(const std::vector& packet_splitted, const std::string& full_packet); 23 | 24 | int x; 25 | int y; 26 | int id; 27 | int map_id; 28 | 29 | std::mutex mtx; 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Scene.cpp: -------------------------------------------------------------------------------- 1 | #include "Scene.h" 2 | 3 | void Scene::on_recv(const std::vector& packet_splitted, const std::string& full_packet) 4 | { 5 | std::string header = packet_splitted[0]; 6 | 7 | if (header == "out") 8 | handle_out(packet_splitted, full_packet); 9 | 10 | else if (header == "drop") 11 | handle_drop(packet_splitted, full_packet); 12 | 13 | else if (header == "get") 14 | handle_get(packet_splitted, full_packet); 15 | 16 | else if (header == "c_map") 17 | handle_cmap(packet_splitted, full_packet); 18 | } 19 | 20 | size_t Scene::get_items_count() const 21 | { 22 | return items.size(); 23 | } 24 | 25 | const std::set Scene::get_items() const 26 | { 27 | return items; 28 | } 29 | 30 | void Scene::handle_out(const std::vector& packet_splitted, const std::string& full_packet) 31 | { 32 | if (packet_splitted.size() < 3) 33 | return; 34 | 35 | int id; 36 | 37 | try 38 | { 39 | id = std::stoi(packet_splitted[2]); 40 | } 41 | 42 | catch (const std::exception& e) 43 | { 44 | std::cerr << "Scene::handle_out " << e.what() << std::endl; 45 | std::cerr << "Packet: " << full_packet << std::endl; 46 | } 47 | 48 | items.erase(id); 49 | } 50 | 51 | void Scene::handle_drop(const std::vector& packet_splitted, const std::string& full_packet) 52 | { 53 | if (packet_splitted.size() < 8) 54 | return; 55 | 56 | int id; 57 | 58 | try 59 | { 60 | id = std::stoi(packet_splitted[2]); 61 | } 62 | 63 | catch (const std::exception& e) 64 | { 65 | std::cerr << "Scene::handle_drop " << e.what() << std::endl; 66 | std::cerr << "Packet: " << full_packet << std::endl; 67 | } 68 | 69 | items.emplace(id); 70 | } 71 | 72 | void Scene::handle_get(const std::vector& packet_splitted, const std::string& full_packet) 73 | { 74 | if (packet_splitted.size() < 5) 75 | return; 76 | 77 | int id; 78 | 79 | try 80 | { 81 | id = std::stoi(packet_splitted[3]); 82 | } 83 | 84 | catch (const std::exception& e) 85 | { 86 | std::cerr << "Scene::handle_get " << e.what() << std::endl; 87 | std::cerr << "Packet: " << full_packet << std::endl; 88 | } 89 | 90 | items.erase(id); 91 | } 92 | 93 | void Scene::handle_cmap(const std::vector& packet_splitted, const std::string& full_packet) 94 | { 95 | items.clear(); 96 | } 97 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Module.h" 4 | #include 5 | #include 6 | 7 | class Scene : public Module 8 | { 9 | public: 10 | void on_recv(const std::vector& packet_splitted, const std::string& full_packet); 11 | 12 | size_t get_items_count() const; 13 | const std::set get_items() const; 14 | 15 | private: 16 | void handle_out(const std::vector& packet_splitted, const std::string& full_packet); 17 | void handle_drop(const std::vector& packet_splitted, const std::string& full_packet); 18 | void handle_get(const std::vector& packet_splitted, const std::string& full_packet); 19 | void handle_cmap(const std::vector& packet_splitted, const std::string& full_packet); 20 | 21 | std::set items; 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Split_string.cpp: -------------------------------------------------------------------------------- 1 | #include "Split_string.h" 2 | 3 | std::vector split_string(const std::string& str, char delimiter) 4 | { 5 | std::vector splitted_string; 6 | 7 | size_t start = 0U; 8 | size_t end = str.find(delimiter); 9 | 10 | while (end != std::string::npos) 11 | { 12 | splitted_string.push_back(str.substr(start, end - start)); 13 | start = end + sizeof(char); 14 | end = str.find(delimiter, start); 15 | } 16 | 17 | splitted_string.push_back(str.substr(start, end)); 18 | 19 | return splitted_string; 20 | } 21 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Split_string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | std::vector split_string(const std::string& str, char delimiter = ' '); 7 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Ts26_Bot.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {f54caf92-4743-4edd-a619-21e6c3d98905} 25 | Ts26Bot 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | stdcpp20 92 | 93 | 94 | Console 95 | true 96 | 97 | 98 | 99 | 100 | Level3 101 | true 102 | true 103 | true 104 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | true 106 | stdcpp20 107 | 108 | 109 | Console 110 | true 111 | true 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | true 119 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 120 | true 121 | stdcpp20 122 | 123 | 124 | Console 125 | true 126 | 127 | 128 | 129 | 130 | Level3 131 | true 132 | true 133 | true 134 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 135 | true 136 | stdcpp20 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/Ts26_Bot.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {4a67f901-ea80-4491-86de-026ca20e68b8} 18 | 19 | 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | PhoenixApi 38 | 39 | 40 | PhoenixApi 41 | 42 | 43 | PhoenixApi 44 | 45 | 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | PhoenixApi 64 | 65 | 66 | PhoenixApi 67 | 68 | 69 | PhoenixApi 70 | 71 | 72 | -------------------------------------------------------------------------------- /cpp/examples/Ts26_Bot/main.cpp: -------------------------------------------------------------------------------- 1 | #include "PhoenixApi/Api.h" 2 | #include "Split_string.h" 3 | #include "Player.h" 4 | #include "Scene.h" 5 | #include "Bot.h" 6 | #include 7 | #include 8 | 9 | int select_port(); 10 | 11 | int main() 12 | { 13 | int port = select_port(); 14 | 15 | if (port == -1) 16 | return 0; 17 | 18 | Phoenix::Api api(port); 19 | Player player; 20 | Scene scene; 21 | Bot bot(&api, &player, &scene); 22 | std::vector modules = { &player, &scene, &bot }; 23 | 24 | std::cout << "Bot is running..." << std::endl; 25 | 26 | while (true) 27 | { 28 | while (!api.empty()) 29 | { 30 | std::string message = api.get_message(); 31 | 32 | try 33 | { 34 | nlohmann::json json_msg = nlohmann::json::parse(message); 35 | 36 | if (json_msg["type"] == Phoenix::Type::packet_send) 37 | { 38 | std::string packet = json_msg["packet"]; 39 | std::vector packet_splitted = split_string(packet); 40 | 41 | if (packet_splitted.size() > 0) 42 | { 43 | for (auto mod : modules) 44 | { 45 | mod->on_send(packet_splitted, packet); 46 | } 47 | } 48 | } 49 | 50 | if (json_msg["type"] == Phoenix::Type::packet_recv) 51 | { 52 | std::string packet = json_msg["packet"]; 53 | std::vector packet_splitted = split_string(packet); 54 | 55 | if (packet_splitted.size() > 0) 56 | { 57 | for (auto mod : modules) 58 | { 59 | mod->on_recv(packet_splitted, packet); 60 | } 61 | } 62 | } 63 | } 64 | 65 | catch (const std::exception& e) 66 | { 67 | std::cerr << e.what() << std::endl; 68 | std::cin.get(); 69 | return 1; 70 | } 71 | } 72 | 73 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 74 | } 75 | 76 | 77 | } 78 | 79 | int select_port() 80 | { 81 | int option = -1; 82 | int port = -1; 83 | 84 | std::vector ports = Phoenix::find_ports(); 85 | 86 | std::cout << "Select the port to connect (-1 to exit):" << std::endl; 87 | 88 | for (size_t i = 0; i < ports.size(); ++i) 89 | { 90 | std::cout << i << ") " << ports[i] << std::endl; 91 | } 92 | 93 | while (true) 94 | { 95 | std::cin >> option; 96 | 97 | if (option == -1) 98 | { 99 | std::cout << "Exiting..." << std::endl; 100 | break; 101 | } 102 | 103 | else if (option < 0 || option >= (int)ports.size()) 104 | { 105 | std::cout << "Selected option is not valid, try again." << std::endl; 106 | std::cout << "Options are: "; 107 | 108 | for (size_t i = 0; i < ports.size(); ++i) 109 | { 110 | std::cout << i << " "; 111 | } 112 | 113 | std::cout << std::endl; 114 | } 115 | 116 | else 117 | { 118 | port = ports[option]; 119 | break; 120 | } 121 | } 122 | 123 | return port; 124 | } 125 | -------------------------------------------------------------------------------- /python/example.py: -------------------------------------------------------------------------------- 1 | from phoenixapi import phoenix, finder 2 | from time import sleep 3 | import json 4 | 5 | if __name__ == "__main__": 6 | # Replace with the actual character name that you want to connect to 7 | api = finder.create_api_from_name("Character name") 8 | 9 | # Logs all the packets that are sent/received from the client 10 | while api.working(): 11 | if not api.empty(): 12 | msg = api.get_message() 13 | json_msg = json.loads(msg) 14 | 15 | if json_msg["type"] == phoenix.Type.packet_send.value: 16 | print("[SEND]: " + json_msg["packet"]) 17 | elif json_msg["type"] == phoenix.Type.packet_recv.value: 18 | print("[RECV]: " + json_msg["packet"]) 19 | else: 20 | sleep(0.01) 21 | 22 | api.close() -------------------------------------------------------------------------------- /python/phoenixapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hatz2/PhoenixAPI/8d5746f5a44b54c8fc1b5cd77c0beff9a76aeff4/python/phoenixapi/__init__.py -------------------------------------------------------------------------------- /python/phoenixapi/finder.py: -------------------------------------------------------------------------------- 1 | import json 2 | from win32gui import EnumWindows, GetWindowText 3 | from re import search 4 | from ctypes.wintypes import HWND, LPARAM 5 | from time import sleep 6 | from phoenixapi import phoenix 7 | 8 | _ports: list[int] = [] 9 | 10 | def find_all_api_ports() -> list[int]: 11 | """Find all ports of the current bot windows.""" 12 | _ports.clear() 13 | EnumWindows(_enum_windows_callback, 0) 14 | return _ports.copy() 15 | 16 | def create_apis_from_names(character_names: list[str]) -> list[tuple[str, phoenix.Api]]: 17 | """ 18 | Create API instances from a list of character names. 19 | 20 | Returns: 21 | list[tuple[str, phoenix.Api]]: A list of tuples containing character names and their corresponding API instances. 22 | 23 | Raises: 24 | RuntimeError: If no bots are running or not all bots with the given character names are found. 25 | """ 26 | 27 | ports = find_all_api_ports() 28 | apis = [] 29 | 30 | if len(ports) == 0: 31 | raise RuntimeError("No bot windows found.") 32 | 33 | for port in ports: 34 | api = phoenix.Api(port) 35 | 36 | # Ask the bot to give us the player info 37 | api.query_player_information() 38 | 39 | # Wait for the bot to respond 40 | while api.working(): 41 | if api.empty(): 42 | sleep(0.01) 43 | continue 44 | 45 | msg = api.get_message() 46 | json_msg = json.loads(msg) 47 | 48 | if json_msg["type"] == phoenix.Type.query_player_info.value: 49 | if json_msg["player_info"]["name"] in character_names: 50 | character_names.remove(json_msg["player_info"]["name"]) 51 | apis.append((json_msg["player_info"]["name"], api)) 52 | break 53 | else: 54 | api.close() 55 | 56 | if (len(character_names) != 0): 57 | raise RuntimeError("Could not find all bots with the given character names.") 58 | 59 | return apis 60 | 61 | def create_api_from_name(character_name: str) -> phoenix.Api: 62 | """ 63 | Create an instance of the API class from the character's name. 64 | 65 | Returns: 66 | phoenix.Api: An instance of the API class. 67 | 68 | Raises: 69 | RuntimeError: If no bot with that name is found or no bots are running. 70 | """ 71 | 72 | ports = find_all_api_ports() 73 | 74 | if len(ports) == 0: 75 | raise RuntimeError("No bot windows found.") 76 | 77 | for port in ports: 78 | api = phoenix.Api(port) 79 | 80 | # Ask the bot to give us the player info 81 | api.query_player_information() 82 | 83 | # Wait for the bot to respond 84 | while api.working(): 85 | if api.empty(): 86 | sleep(0.01) 87 | continue 88 | 89 | msg = api.get_message() 90 | json_msg = json.loads(msg) 91 | 92 | if json_msg["type"] == phoenix.Type.query_player_info.value: 93 | if json_msg["player_info"]["name"] == character_name: 94 | return api 95 | else: 96 | api.close() 97 | 98 | raise RuntimeError(f"Could not find bot with character name: {character_name}") 99 | 100 | def _enum_windows_callback(hwnd: HWND, lparam: LPARAM) -> bool: 101 | """Callback function to enumerate windows and check if it is a bot window.""" 102 | window_title = GetWindowText(hwnd) 103 | 104 | if "- Phoenix Bot" in window_title: 105 | match: str = search(r"Bot:(\d+)", window_title) 106 | 107 | if match: 108 | port = (int)(match.group(1)) 109 | _ports.append(port) 110 | 111 | return True -------------------------------------------------------------------------------- /python/phoenixapi/phoenix.py: -------------------------------------------------------------------------------- 1 | import queue 2 | import socket 3 | import threading 4 | import json 5 | import enum 6 | 7 | class Type(enum.Enum): 8 | packet_send = 0 9 | packet_recv = 1 10 | attack = 2 11 | player_skill = 3 12 | player_walk = 4 13 | pet_skill = 5 14 | partner_skill = 6 15 | pets_walk = 7 16 | pick_up = 8 17 | collect = 9 18 | start_bot = 10 19 | stop_bot = 11 20 | continue_bot = 12 21 | load_settings = 13 22 | start_minigame_bot = 14 23 | stop_minigame_bot = 15 24 | query_player_info = 16 25 | query_inventory = 17 26 | query_skills_info = 18 27 | query_map_entities = 19 28 | target_entity = 20 29 | 30 | class Api: 31 | HOST = "127.0.0.1" 32 | 33 | def __init__(self, port : int) -> None: 34 | self._socket = socket.socket() 35 | self._socket.connect((Api.HOST, port)) 36 | 37 | self._do_work = True 38 | self._messages = queue.Queue() 39 | self._worker = threading.Thread(target = self._work) 40 | self._worker.start() 41 | 42 | def _send_data(self, data : str) -> int: 43 | buffer = data + '\1' 44 | return self._socket.send(buffer.encode()) 45 | 46 | 47 | def _work(self) -> None: 48 | buffer_size = 4096 49 | data = b'' 50 | delim_char = b'\1' 51 | 52 | while self._do_work: 53 | buffer = self._socket.recv(buffer_size) 54 | 55 | if (len(buffer) <= 0): 56 | break 57 | 58 | data += buffer 59 | delim_pos = data.find(delim_char) 60 | 61 | while delim_pos != -1: 62 | msg = data[0:delim_pos] 63 | data = data[delim_pos + 1:] 64 | decoded_msg = msg.decode() 65 | self._messages.put(decoded_msg) 66 | delim_pos = data.find(delim_char) 67 | 68 | def working(self) -> bool: 69 | return self._worker.is_alive() 70 | 71 | def close(self) -> None: 72 | if self.working(): 73 | self._do_work = False 74 | self._worker.join() 75 | 76 | def get_message(self) -> str: 77 | if self._messages.empty(): 78 | return "" 79 | 80 | return self._messages.get() 81 | 82 | def empty(self) -> bool: 83 | return self._messages.empty() 84 | 85 | def send_packet(self, packet : str) -> bool: 86 | data = { 87 | "type" : Type.packet_send.value, 88 | "packet" : packet 89 | } 90 | 91 | json_data = json.dumps(data) 92 | 93 | return self._send_data(json_data) == len(json_data) + 1 94 | 95 | def recv_packet(self, packet : str) -> bool: 96 | data = { 97 | "type" : Type.packet_recv.value, 98 | "packet" : packet 99 | } 100 | 101 | json_data = json.dumps(data) 102 | 103 | return self._send_data(json_data) == len(json_data) + 1 104 | 105 | def attack_monster(self, monster_id : int) -> bool: 106 | data = { 107 | "type" : Type.attack.value, 108 | "monster_id" : monster_id 109 | } 110 | 111 | json_data = json.dumps(data) 112 | 113 | return self._send_data(json_data) == len(json_data) + 1 114 | 115 | def use_player_skill(self, monster_id : int, skill_id : int) -> bool: 116 | data = { 117 | "type" : Type.player_skill.value, 118 | "monster_id" : monster_id, 119 | "skill_id" : skill_id 120 | } 121 | 122 | json_data = json.dumps(data) 123 | 124 | return self._send_data(json_data) == len(json_data) + 1 125 | 126 | def player_walk(self, x : int, y : int) -> bool: 127 | data = { 128 | "type" : Type.player_walk.value, 129 | "x" : x, 130 | "y" : y 131 | } 132 | 133 | json_data = json.dumps(data) 134 | 135 | return self._send_data(json_data) == len(json_data) + 1 136 | 137 | def use_pet_skill(self, monster_id : int, skill_id : int) -> bool: 138 | data = { 139 | "type" : Type.pet_skill.value, 140 | "monster_id" : monster_id, 141 | "skill_id" : skill_id 142 | } 143 | 144 | json_data = json.dumps(data) 145 | 146 | return self._send_data(json_data) == len(json_data) + 1 147 | 148 | def use_partner_skill(self, monster_id : int, skill_id : int) -> bool: 149 | data = { 150 | "type" : Type.partner_skill.value, 151 | "monster_id" : monster_id, 152 | "skill_id" : skill_id 153 | } 154 | 155 | json_data = json.dumps(data) 156 | 157 | return self._send_data(json_data) == len(json_data) + 1 158 | 159 | def pets_walk(self, x : int, y : int) -> bool: 160 | data = { 161 | "type" : Type.pets_walk.value, 162 | "x" : x, 163 | "y" : y 164 | } 165 | 166 | json_data = json.dumps(data) 167 | 168 | return self._send_data(json_data) == len(json_data) + 1 169 | 170 | def pick_up(self, item_id : int) -> bool: 171 | data = { 172 | "type" : Type.pick_up.value, 173 | "item_id" : item_id 174 | } 175 | 176 | json_data = json.dumps(data) 177 | 178 | return self._send_data(json_data) == len(json_data) + 1 179 | 180 | def collect(self, npc_id : int) -> bool: 181 | data = { 182 | "type" : Type.collect.value, 183 | "npc_id" : npc_id 184 | } 185 | 186 | json_data = json.dumps(data) 187 | 188 | return self._send_data(json_data) == len(json_data) + 1 189 | 190 | def start_bot(self) -> bool: 191 | data = { 192 | "type" : Type.start_bot.value 193 | } 194 | 195 | json_data = json.dumps(data) 196 | 197 | return self._send_data(json_data) == len(json_data) + 1 198 | 199 | def stop_bot(self) -> bool: 200 | data = { 201 | "type" : Type.stop_bot.value 202 | } 203 | 204 | json_data = json.dumps(data) 205 | 206 | return self._send_data(json_data) == len(json_data) + 1 207 | 208 | def continue_bot(self) -> bool: 209 | data = { 210 | "type" : Type.continue_bot.value 211 | } 212 | 213 | json_data = json.dumps(data) 214 | 215 | return self._send_data(json_data) == len(json_data) + 1 216 | 217 | def load_settings(self, settings_path : str) -> bool: 218 | data = { 219 | "type" : Type.load_settings.value, 220 | "path" : settings_path 221 | } 222 | 223 | json_data = json.dumps(data) 224 | 225 | return self._send_data(json_data) == len(json_data) + 1 226 | 227 | def start_minigame_bot(self) -> bool: 228 | data = { 229 | "type" : Type.start_minigame_bot.value 230 | } 231 | 232 | json_data = json.dumps(data) 233 | 234 | return self._send_data(json_data) == len(json_data) + 1 235 | 236 | def stop_minigame_bot(self) -> bool: 237 | data = { 238 | "type" : Type.stop_minigame_bot.value 239 | } 240 | 241 | json_data = json.dumps(data) 242 | 243 | return self._send_data(json_data) == len(json_data) + 1 244 | 245 | def query_player_information(self) -> bool: 246 | data = { 247 | "type" : Type.query_player_info.value 248 | } 249 | 250 | json_data = json.dumps(data) 251 | 252 | return self._send_data(json_data) == len(json_data) + 1 253 | 254 | def query_inventory(self) -> bool: 255 | data = { 256 | "type" : Type.query_inventory.value 257 | } 258 | 259 | json_data = json.dumps(data) 260 | 261 | return self._send_data(json_data) == len(json_data) + 1 262 | 263 | def query_skills_info(self) -> bool: 264 | data = { 265 | "type" : Type.query_skills_info.value 266 | } 267 | 268 | json_data = json.dumps(data) 269 | 270 | return self._send_data(json_data) == len(json_data) + 1 271 | 272 | def query_map_entities(self) -> bool: 273 | data = { 274 | "type" : Type.query_map_entities.value 275 | } 276 | 277 | json_data = json.dumps(data) 278 | 279 | return self._send_data(json_data) == len(json_data) + 1 280 | 281 | def target_entity(self, entity_id : int, entity_type: int): 282 | data = { 283 | "type" : Type.target_entity.value, 284 | "entity_id" : entity_id, 285 | "entity_type" : entity_type 286 | } 287 | 288 | json_data = json.dumps(data) 289 | 290 | return self._send_data(json_data) == len(json_data) + 1 --------------------------------------------------------------------------------