├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── src ├── main │ └── java │ │ └── com │ │ └── rs │ │ ├── plugin │ │ ├── EventListener.java │ │ ├── event │ │ │ ├── ReportAbuseEvent.java │ │ │ ├── PlayerLoggedOutEvent.java │ │ │ ├── PlayerEvent.java │ │ │ ├── PlayerLoggedInEvent.java │ │ │ ├── ActionButtonEvent.java │ │ │ ├── PublicMessageEvent.java │ │ │ ├── ModifyPlayerListEvent.java │ │ │ ├── CommandEvent.java │ │ │ ├── PrivateMessageEvent.java │ │ │ └── ModifyChatModeEvent.java │ │ ├── Event.java │ │ ├── listener │ │ │ ├── TickListener.java │ │ │ ├── PluginStateListener.java │ │ │ ├── CommandListener.java │ │ │ ├── ReportAbuseListener.java │ │ │ ├── ActionButtonListener.java │ │ │ ├── MessageListener.java │ │ │ ├── PlayerConnectivityListener.java │ │ │ └── MessageConfigListener.java │ │ ├── Bootstrap.java │ │ └── PluginHandler.java │ │ ├── util │ │ ├── Tickable.java │ │ ├── OverflowException.java │ │ ├── AbstractCredentialValidator.java │ │ ├── LenientCredentialValidator.java │ │ └── WeaponDefinition.java │ │ ├── service │ │ ├── Service.java │ │ ├── GameService.java │ │ └── NetworkService.java │ │ ├── io │ │ ├── JsonUtils.java │ │ ├── PlayerFileHandler.java │ │ └── JsonPlayerFileHandler.java │ │ ├── entity │ │ ├── action │ │ │ ├── Animation.java │ │ │ ├── Graphics.java │ │ │ ├── Hit.java │ │ │ ├── PublicChat.java │ │ │ └── AsyncMovement.java │ │ ├── player │ │ │ ├── infractions │ │ │ │ ├── ReportAbuse.java │ │ │ │ ├── ReportAbuseRule.java │ │ │ │ └── PlayerInfractions.java │ │ │ ├── skills │ │ │ │ ├── SkillType.java │ │ │ │ ├── Skill.java │ │ │ │ └── Skills.java │ │ │ ├── PlayerSettings.java │ │ │ └── PlayerUpdateContext.java │ │ ├── npc │ │ │ ├── NpcUpdateContext.java │ │ │ └── Npc.java │ │ ├── Entity.java │ │ ├── EntityUpdateContext.java │ │ ├── MovementHandler.java │ │ └── Position.java │ │ ├── net │ │ ├── ConnectionThrottle.java │ │ └── HostGateway.java │ │ ├── Settings.java │ │ ├── task │ │ ├── TaskHandler.java │ │ └── Task.java │ │ ├── ServerModule.java │ │ ├── Server.java │ │ └── WorldHandler.java └── test │ └── java │ └── com │ └── rs │ └── entity │ └── player │ └── skills │ └── SkillsTest.java ├── data ├── settings.json ├── stackable.dat └── characters │ └── mopar.json ├── CHANGELOG ├── plugins ├── scripts │ ├── PublicMessaging.groovy │ ├── CharacterDesign.groovy │ ├── ReportAbuse.groovy │ ├── Emotes.groovy │ ├── PlayerSettings.groovy │ └── Infractions.groovy ├── SamplePlugin.groovy ├── packets │ ├── ActionButtonHandler.groovy │ └── CommandHandler.groovy └── GroovyBootstrap.groovy ├── USEGUIDE.md ├── gradlew.bat ├── README.md └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'runesource' 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dozmus/runesource/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDEA project files 2 | out 3 | build 4 | .idea 5 | .gradle 6 | META-INF 7 | runesource.iml 8 | 9 | # Server log files 10 | data/logs 11 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/EventListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin; 2 | 3 | /** 4 | * An event listener, all listeners which correspond to events must extend this. 5 | */ 6 | public interface EventListener { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /data/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "@type":"com.rs.Settings", 3 | "serverName":"RuneSource", 4 | "startPosition":{ 5 | "x":3222, 6 | "y":3222, 7 | "z":0 8 | }, 9 | "maxConsPerHost": 2, 10 | "hashPasswords": false, 11 | "dateFormat": "dd-MM-yyyy HH:mm:ss" 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/ReportAbuseEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | 3 | import com.rs.entity.player.Player; 4 | import com.rs.entity.player.infractions.ReportAbuse; 5 | 6 | /** 7 | * A report abuse event. 8 | */ 9 | public class ReportAbuseEvent extends PlayerEvent { 10 | 11 | private final ReportAbuse reportAbuse; 12 | 13 | public ReportAbuseEvent(Player player, ReportAbuse reportAbuse) { 14 | super(player); 15 | this.reportAbuse = reportAbuse; 16 | } 17 | 18 | public ReportAbuse getReportAbuse() { 19 | return reportAbuse; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/Event.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | /** 20 | * An event. 21 | */ 22 | public interface Event { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/rs/util/Tickable.java: -------------------------------------------------------------------------------- 1 | package com.rs.util; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public interface Tickable { 20 | 21 | void tick() throws Exception; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/listener/TickListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.listener; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.EventListener; 20 | import com.rs.util.Tickable; 21 | 22 | public interface TickListener extends Tickable, EventListener { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/rs/service/Service.java: -------------------------------------------------------------------------------- 1 | package com.rs.service; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.util.Tickable; 20 | 21 | public interface Service extends Tickable { 22 | 23 | void init() throws Exception; 24 | 25 | void tick(); 26 | 27 | void cleanup(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/listener/PluginStateListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.listener; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.EventListener; 20 | 21 | public interface PluginStateListener extends EventListener { 22 | 23 | void loaded(); 24 | 25 | void unloaded(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/listener/CommandListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.listener; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.EventListener; 20 | import com.rs.plugin.event.CommandEvent; 21 | 22 | public interface CommandListener extends EventListener { 23 | 24 | void command(CommandEvent e); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/listener/ReportAbuseListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.listener; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.EventListener; 20 | import com.rs.plugin.event.ReportAbuseEvent; 21 | 22 | public interface ReportAbuseListener extends EventListener { 23 | 24 | void reportAbuse(ReportAbuseEvent e); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/listener/ActionButtonListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.listener; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.EventListener; 20 | import com.rs.plugin.event.ActionButtonEvent; 21 | 22 | public interface ActionButtonListener extends EventListener { 23 | 24 | void actionButton(ActionButtonEvent e); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/rs/io/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.rs.io; 2 | 3 | import com.cedarsoftware.util.io.JsonWriter; 4 | 5 | import java.io.File; 6 | import java.io.FileWriter; 7 | import java.io.IOException; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class JsonUtils { 12 | 13 | private static final Map WRITER_ARGS = new HashMap<>(); 14 | 15 | static { 16 | WRITER_ARGS.put("JsonWriter.PRETTY_PRINT", true); 17 | } 18 | 19 | public static void write(String fileName, boolean overwrite, Object object) throws IOException { 20 | File file = new File(fileName); 21 | 22 | // Overwrite 23 | if (overwrite && file.exists()) { 24 | file.delete(); 25 | } 26 | 27 | // Generating pretty json 28 | String json = JsonWriter.objectToJson(object, WRITER_ARGS); 29 | json = JsonWriter.formatJson(json); 30 | 31 | // Writing json 32 | FileWriter writer = new FileWriter(file); 33 | writer.write(json); 34 | writer.close(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/PlayerLoggedOutEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | 21 | /** 22 | * Player logged out event. 23 | */ 24 | public final class PlayerLoggedOutEvent extends PlayerEvent { 25 | 26 | public PlayerLoggedOutEvent(Player player) { 27 | super(player); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v1.0.2 (unreleased) 2 | * Gradle build system (#30) 3 | * Bump json-io v3.3.1 -> v4.9.12 4 | * Bump groovy v2.4.3 -> v2.5.2 5 | * Multi-threaded updating (courtesy @lare96) 6 | * Fix total level and combat level (#28) - NOTE: In the default RS #317 client, farming is disabled client-side. 7 | * Revert java target from 1.10 to 1.8 (bug introduced in #30) 8 | 9 | v1.0.1 10 | * Powerful Plugins using Groovy (#2) 11 | * Task System 12 | * Full Player Support (#8) 13 | * Full Npc Support (#11) 14 | * Friends List and Private Messaging (with privacy settings) (#4, #5, #17) 15 | * JSON Data Serialization (optional: passwords are hashed using SHA-256) 16 | * Banning and muting with expiration dates (#18, #19) 17 | * Misc stuff 18 | * Validate equipped items, weapon stack merging, and correct weapon interfaces (#9) 19 | * Emotes 20 | * Running 21 | * Persistent settings (#6) 22 | * Buffer caching (#10) 23 | * Login attempt throttling (#13) 24 | * Report abuse interface (#7) 25 | * Saves all players on shutdown (e.g. if you use Ctrl+C) (#25) 26 | * Commands - ::hide ::unhide (#27) 27 | 28 | v1.0.0 29 | The original RuneSource project. 30 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/listener/MessageListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.listener; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.EventListener; 20 | import com.rs.plugin.event.PrivateMessageEvent; 21 | import com.rs.plugin.event.PublicMessageEvent; 22 | 23 | public interface MessageListener extends EventListener { 24 | 25 | void publicMessage(PublicMessageEvent e); 26 | 27 | void privateMessage(PrivateMessageEvent e); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/rs/util/OverflowException.java: -------------------------------------------------------------------------------- 1 | package com.rs.util; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public final class OverflowException extends RuntimeException { 20 | 21 | private final int remainder; 22 | 23 | public OverflowException(int remainder) { 24 | super("Overflow by: " + remainder); 25 | this.remainder = remainder; 26 | } 27 | 28 | public int getRemainder() { 29 | return remainder; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/listener/PlayerConnectivityListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.listener; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.EventListener; 20 | import com.rs.plugin.event.PlayerLoggedInEvent; 21 | import com.rs.plugin.event.PlayerLoggedOutEvent; 22 | 23 | public interface PlayerConnectivityListener extends EventListener { 24 | 25 | void logIn(PlayerLoggedInEvent e); 26 | 27 | void logOut(PlayerLoggedOutEvent e); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/rs/util/AbstractCredentialValidator.java: -------------------------------------------------------------------------------- 1 | package com.rs.util; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public abstract class AbstractCredentialValidator { 20 | 21 | public boolean validate(String username, String password) { 22 | return validateUsername(username) && validatePassword(password); 23 | } 24 | 25 | public abstract boolean validateUsername(String username); 26 | 27 | public abstract boolean validatePassword(String password); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/action/Animation.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.action; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public final class Animation { 20 | 21 | private final int id; 22 | private final int delay; 23 | 24 | public Animation(int id, int delay) { 25 | this.id = id; 26 | this.delay = delay; 27 | } 28 | 29 | public int getId() { 30 | return id; 31 | } 32 | 33 | public int getDelay() { 34 | return delay; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/action/Graphics.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.action; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public final class Graphics { 20 | 21 | private final int id; 22 | private final int delay; 23 | 24 | public Graphics(int id, int delay) { 25 | this.id = id; 26 | this.delay = delay; 27 | } 28 | 29 | public int getId() { 30 | return id; 31 | } 32 | 33 | public int getDelay() { 34 | return delay; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/action/Hit.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.action; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public final class Hit { 20 | 21 | private final int damage; 22 | private final int type; // XXX document types 23 | 24 | public Hit(int damage, int type) { 25 | this.damage = damage; 26 | this.type = type; 27 | } 28 | 29 | public int getDamage() { 30 | return damage; 31 | } 32 | 33 | public int getType() { 34 | return type; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/service/GameService.java: -------------------------------------------------------------------------------- 1 | package com.rs.service; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.WorldHandler; 20 | 21 | public class GameService implements Service { 22 | 23 | public void init() throws Exception { 24 | } 25 | 26 | public void tick() { 27 | try { 28 | WorldHandler.getInstance().tick(); 29 | } catch (Exception ex) { 30 | ex.printStackTrace(); 31 | } 32 | } 33 | 34 | public void cleanup() { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/PlayerEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | import com.rs.plugin.Event; 21 | 22 | /** 23 | * An {@link Event} triggered by a {@link Player}. 24 | */ 25 | public abstract class PlayerEvent implements Event { 26 | 27 | protected final Player player; 28 | 29 | public PlayerEvent(Player player) { 30 | this.player = player; 31 | } 32 | 33 | public Player getPlayer() { 34 | return player; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/PlayerLoggedInEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | 21 | /** 22 | * Player logged in event. 23 | */ 24 | public final class PlayerLoggedInEvent extends PlayerEvent { 25 | 26 | private final boolean newPlayer; 27 | 28 | public PlayerLoggedInEvent(Player player, boolean newPlayer) { 29 | super(player); 30 | this.newPlayer = newPlayer; 31 | } 32 | 33 | public boolean isNewPlayer() { 34 | return newPlayer; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/Bootstrap.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.listener.*; 20 | 21 | /** 22 | * A bootstrap, which dispatches events from the core of the server to third-party plugins. 23 | * 24 | * @author Pure_ 25 | */ 26 | public interface Bootstrap extends ActionButtonListener, CommandListener, MessageConfigListener, 27 | MessageListener, PlayerConnectivityListener, PluginStateListener, ReportAbuseListener, TickListener { 28 | 29 | /** 30 | * Loads its dependencies. 31 | */ 32 | void load(); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/ActionButtonEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | 21 | /** 22 | * An action button click event. 23 | */ 24 | public final class ActionButtonEvent extends PlayerEvent { 25 | 26 | private final int actionButtonId; 27 | 28 | public ActionButtonEvent(Player player, int actionButtonId) { 29 | super(player); 30 | this.actionButtonId = actionButtonId; 31 | } 32 | 33 | public int getActionButtonId() { 34 | return actionButtonId; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/PublicMessageEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.action.PublicChat; 20 | import com.rs.entity.player.Player; 21 | 22 | /** 23 | * Public message event. 24 | */ 25 | public final class PublicMessageEvent extends PlayerEvent { 26 | 27 | private final PublicChat publicChat; 28 | 29 | public PublicMessageEvent(Player player, PublicChat publicChat) { 30 | super(player); 31 | this.publicChat = publicChat; 32 | } 33 | 34 | public PublicChat getPublicChat() { 35 | return publicChat; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/listener/MessageConfigListener.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.listener; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.plugin.EventListener; 20 | import com.rs.plugin.event.ModifyChatModeEvent; 21 | import com.rs.plugin.event.ModifyPlayerListEvent; 22 | 23 | public interface MessageConfigListener extends EventListener { 24 | 25 | void modifyChatMode(ModifyChatModeEvent e); 26 | 27 | void addFriend(ModifyPlayerListEvent e); 28 | 29 | void removeFriend(ModifyPlayerListEvent e); 30 | 31 | void addIgnore(ModifyPlayerListEvent e); 32 | 33 | void removeIgnore(ModifyPlayerListEvent e); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/ModifyPlayerListEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | 21 | /** 22 | * An event representing a username in hashed format. 23 | */ 24 | public final class ModifyPlayerListEvent extends PlayerEvent { 25 | 26 | private final long target; 27 | 28 | public ModifyPlayerListEvent(Player player, long target) { 29 | super(player); 30 | this.target = target; 31 | } 32 | 33 | /** 34 | * The player's username id who was added or removed from the friends list. 35 | */ 36 | public long getTarget() { 37 | return target; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/scripts/PublicMessaging.groovy: -------------------------------------------------------------------------------- 1 | import com.rs.entity.player.Player 2 | import com.rs.plugin.event.PrivateMessageEvent 3 | import com.rs.plugin.event.PublicMessageEvent 4 | import com.rs.plugin.listener.MessageListener 5 | 6 | /* 7 | * This file is part of RuneSource. 8 | * 9 | * RuneSource is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * RuneSource is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with RuneSource. If not, see . 21 | */ 22 | 23 | class PublicMessaging implements MessageListener { 24 | 25 | void publicMessage(PublicMessageEvent e) { 26 | Player player = e.getPlayer() 27 | 28 | if (!player.getAttributes().getInfractions().isMuted()) { 29 | player.setPublicChat e.getPublicChat() 30 | player.getUpdateContext().setPublicChatUpdateRequired() 31 | } 32 | } 33 | 34 | void privateMessage(PrivateMessageEvent e) { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/action/PublicChat.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.action; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public final class PublicChat { 20 | 21 | private final int color; // XXX document color 22 | private final int effects; // XXX document effects 23 | private final byte[] text; 24 | 25 | public PublicChat(int color, int effects, byte[] text) { 26 | this.color = color; 27 | this.effects = effects; 28 | this.text = text; 29 | } 30 | 31 | public int getColor() { 32 | return color; 33 | } 34 | 35 | public int getEffects() { 36 | return effects; 37 | } 38 | 39 | public byte[] getText() { 40 | return text; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/CommandEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | 21 | /** 22 | * A player command event. 23 | */ 24 | public final class CommandEvent extends PlayerEvent { 25 | 26 | private final String commandName; 27 | private final String[] args; 28 | 29 | public CommandEvent(Player player, String commandName, String[] args) { 30 | super(player); 31 | this.commandName = commandName; 32 | this.args = args; 33 | } 34 | 35 | public String getCommandName() { 36 | return commandName; 37 | } 38 | 39 | public String[] getArgs() { 40 | return args; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/rs/io/PlayerFileHandler.java: -------------------------------------------------------------------------------- 1 | package com.rs.io; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.PlayerAttributes; 20 | 21 | /** 22 | * A player loader, and saver. 23 | * 24 | * @author Pure_ 25 | */ 26 | public interface PlayerFileHandler { 27 | 28 | /** 29 | * Saves the argument attributes. 30 | */ 31 | void save(PlayerAttributes attributes) throws Exception; 32 | 33 | /** 34 | * Returns the player attributes associated with the argument username. 35 | */ 36 | PlayerAttributes load(String username) throws Exception; 37 | 38 | /** 39 | * Returns the directory this will store player flat files in. 40 | */ 41 | String getStorageDirectory(); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/PrivateMessageEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | 21 | /** 22 | * Private message event. 23 | */ 24 | public final class PrivateMessageEvent extends PlayerEvent { 25 | 26 | private final long target; 27 | private final byte[] text; 28 | 29 | public PrivateMessageEvent(Player player, long target, byte[] text) { 30 | super(player); 31 | this.target = target; 32 | this.text = text; 33 | } 34 | 35 | /** 36 | * The player's username id who is being messaged. 37 | */ 38 | public long getTarget() { 39 | return target; 40 | } 41 | 42 | public byte[] getText() { 43 | return text; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/player/infractions/ReportAbuse.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.player.infractions; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public final class ReportAbuse { 20 | 21 | private final String username; 22 | private final ReportAbuseRule rule; 23 | private final boolean muteFor48Hours; 24 | 25 | public ReportAbuse(String username, ReportAbuseRule rule, boolean muteFor48Hours) { 26 | this.username = username; 27 | this.rule = rule; 28 | this.muteFor48Hours = muteFor48Hours; 29 | } 30 | 31 | public String getUsername() { 32 | return username; 33 | } 34 | 35 | public ReportAbuseRule getRule() { 36 | return rule; 37 | } 38 | 39 | public boolean isMuteFor48Hours() { 40 | return muteFor48Hours; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/player/skills/SkillType.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.player.skills; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public enum SkillType { 20 | 21 | ATTACK(true), 22 | DEFENCE(true), 23 | STRENGTH(true), 24 | HITPOINTS(true), 25 | RANGED(true), 26 | PRAYER(true), 27 | MAGIC(true), 28 | COOKING, 29 | WOODCUTTING, 30 | FLETCHING, 31 | FISHING, 32 | FIREMAKING, 33 | CRAFTING, 34 | SMITHING, 35 | MINING, 36 | HERBLORE, 37 | AGILITY, 38 | THIEVING, 39 | SLAYER, 40 | FARMING, 41 | RUNECRAFTING; 42 | 43 | private final boolean combatSkill; 44 | 45 | SkillType() { 46 | this(false); 47 | } 48 | 49 | SkillType(boolean combatSkill) { 50 | this.combatSkill = combatSkill; 51 | } 52 | 53 | public boolean isCombatSkill() { 54 | return combatSkill; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /USEGUIDE.md: -------------------------------------------------------------------------------- 1 | # USEGUIDE 2 | 3 | ## What is the 'mopar' account's password? 4 | `test` 5 | 6 | ## Modifying server variables 7 | You can change the: 8 | * Server name 9 | * Starting position 10 | * Maximum number of connections per host 11 | * Whether passwords are hashed 12 | 13 | by editting the server config file in `/data/settings.json`. 14 | 15 | Note: If you change the hash passwords variable any existing accounts will be 16 | rendered inaccessible, unless if you change it back. 17 | 18 | ## How to save a new variable in a player file? 19 | Add a new variable into the PlayerAttributes class (under com.rs.entity.player). 20 | The next time a player logs in and out it their save file will be updated with no problems! 21 | 22 | ## How to add a new command? 23 | Add any commands into the Groovy script in the CommandHandler class (under /plugins/bindings/packets/). 24 | 25 | ## How to add a new plugin? 26 | Create a Groovy class file (i.e. something `.groovy`) extending an `EventListener` (or multiple) under `/plugins/`. 27 | You can find a full list of listeners under `com.rs.plugin.listener`. 28 | Each listener will give your script some information regarding the server. 29 | 30 | ## How to create a new listener? 31 | Firstly, create a new subclass of `EventListener` in `com.rs.plugin.listener`. 32 | 33 | Next, add this interface to the `Bootstrap` interface. 34 | Make sure you implement and handle this method, analogously to the others in `GroovyBootstrap`. 35 | 36 | Finally, create a corresponding static `dispatch*` in `PluginHandler`. 37 | Make sure that this dispatch method is called whenever this event occurs. 38 | -------------------------------------------------------------------------------- /plugins/SamplePlugin.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | import com.rs.plugin.listener.PluginStateListener 18 | import com.rs.plugin.listener.TickListener 19 | 20 | /** 21 | * A sample plugin, which extends TickListener which tells it when a server tick occurs, and PluginStateListener 22 | * which tells it when it is loaded or unloaded. 23 | * It can know when more events occur by implementing interfaces from the com.rs.plugin.listener package. 24 | */ 25 | class SamplePlugin implements TickListener, PluginStateListener { 26 | 27 | @Override 28 | void tick() throws Exception { 29 | // Code to execute on tick 30 | } 31 | 32 | @Override 33 | void loaded() throws Exception { 34 | // Code to execute when plugin is enabled 35 | } 36 | 37 | @Override 38 | void unloaded() throws Exception { 39 | // Code to execute when plugin is disabled 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /plugins/packets/ActionButtonHandler.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | import com.rs.entity.player.Player 18 | import com.rs.plugin.event.ActionButtonEvent 19 | import com.rs.plugin.listener.ActionButtonListener 20 | 21 | class ActionButtonHandler implements ActionButtonListener { 22 | 23 | void actionButton(ActionButtonEvent evt) { 24 | Player player = evt.getPlayer() 25 | 26 | switch (evt.getActionButtonId()) { 27 | case 9154: 28 | player.sendLogout() 29 | break 30 | case 153: 31 | player.getAttributes().getSettings().setRunToggled true 32 | break 33 | case 152: 34 | player.getAttributes().getSettings().setRunToggled false 35 | break 36 | default: 37 | println "Unhandled button: ${evt.getActionButtonId()}" 38 | break 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/rs/net/ConnectionThrottle.java: -------------------------------------------------------------------------------- 1 | package com.rs.net; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | /** 23 | * A failed connection attempt recorder, used to throttle failed login attempts. 24 | * Note: The cool down is not 100% accurate. 25 | */ 26 | public final class ConnectionThrottle { 27 | 28 | public static final int COOLDOWN = 60_000; 29 | private static final int MAX_ATTEMPTS = 5; 30 | private static final Map map = new HashMap<>(); 31 | 32 | public static void enter(String host) { 33 | if (map.containsKey(host)) { 34 | map.put(host, map.get(host) + 1); 35 | } else { 36 | map.put(host, 1); 37 | } 38 | } 39 | 40 | public static boolean throttled(String host) { 41 | return map.containsKey(host) && map.get(host) >= MAX_ATTEMPTS; 42 | } 43 | 44 | public static void clear() { 45 | map.clear(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/event/ModifyChatModeEvent.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin.event; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | 21 | /** 22 | * An event representing a change in chat mode. 23 | */ 24 | public final class ModifyChatModeEvent extends PlayerEvent { 25 | 26 | private final int publicChatMode; 27 | private final int privateChatMode; 28 | private final int tradeMode; 29 | 30 | public ModifyChatModeEvent(Player player, int publicChatMode, int privateChatMode, int tradeMode) { 31 | super(player); 32 | this.publicChatMode = publicChatMode; 33 | this.privateChatMode = privateChatMode; 34 | this.tradeMode = tradeMode; 35 | } 36 | 37 | public int getPublicChatMode() { 38 | return publicChatMode; 39 | } 40 | 41 | public int getPrivateChatMode() { 42 | return privateChatMode; 43 | } 44 | 45 | public int getTradeMode() { 46 | return tradeMode; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/player/infractions/ReportAbuseRule.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.player.infractions; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public enum ReportAbuseRule { 20 | OffensiveLanguage(0), 21 | ItemScamming(1), 22 | PasswordScamming(2), 23 | BugAbuse(3), 24 | JagexStaffImpersonation(4), 25 | AccountSharingOrTrading(5), 26 | Macroing(6), 27 | MultipleLoggingIn(7), 28 | EncouragingRuleBreaking(8), 29 | MisuseOfCustomerSupport(9), 30 | Advertising(10), 31 | RealWorldItemTrading(11); 32 | 33 | private final int ruleId; 34 | 35 | ReportAbuseRule(int ruleId) { 36 | this.ruleId = ruleId; 37 | } 38 | 39 | public static ReportAbuseRule ofId(int ruleId) { 40 | for (ReportAbuseRule rule : values()) { 41 | if (rule.getRuleId() == ruleId) { 42 | return rule; 43 | } 44 | } 45 | throw new IllegalArgumentException("invalid rule id"); 46 | } 47 | 48 | public int getRuleId() { 49 | return ruleId; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /plugins/scripts/CharacterDesign.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | import com.rs.entity.player.Player 18 | import com.rs.plugin.event.ActionButtonEvent 19 | import com.rs.plugin.event.PlayerLoggedInEvent 20 | import com.rs.plugin.event.PlayerLoggedOutEvent 21 | import com.rs.plugin.listener.ActionButtonListener 22 | import com.rs.plugin.listener.PlayerConnectivityListener 23 | 24 | class CharacterDesign implements PlayerConnectivityListener, ActionButtonListener { 25 | 26 | void actionButton(ActionButtonEvent evt) { 27 | Player player = evt.getPlayer() 28 | 29 | if (evt.getActionButtonId() == 14067) { // Design character interface accept button 30 | player.sendClearScreen() 31 | player.setCurrentInterfaceId(-1) 32 | } 33 | } 34 | 35 | void logIn(PlayerLoggedInEvent evt) { 36 | Player player = evt.getPlayer() 37 | 38 | if (evt.isNewPlayer()) { 39 | player.sendInterface 3559 40 | player.setCurrentInterfaceId 3559 41 | } 42 | } 43 | 44 | void logOut(PlayerLoggedOutEvent evt) { 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/player/skills/Skill.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.player.skills; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | public class Skill { 20 | 21 | private final SkillType type; 22 | private int level; 23 | private int maxLevel; 24 | private int experience; 25 | 26 | public Skill(SkillType type) { 27 | this(type, 1, 1, 0); 28 | } 29 | 30 | public Skill(SkillType type, int level, int maxLevel, int experience) { 31 | this.type = type; 32 | this.level = level; 33 | this.maxLevel = maxLevel; 34 | this.experience = experience; 35 | } 36 | 37 | public SkillType getType() { 38 | return type; 39 | } 40 | 41 | public int getLevel() { 42 | return level; 43 | } 44 | 45 | public void setLevel(int level) { 46 | this.level = level; 47 | } 48 | 49 | public int getMaxLevel() { 50 | return maxLevel; 51 | } 52 | 53 | public void setMaxLevel(int maxLevel) { 54 | this.maxLevel = maxLevel; 55 | } 56 | 57 | public int getExperience() { 58 | return experience; 59 | } 60 | 61 | public void setExperience(int experience) { 62 | this.experience = experience; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/action/AsyncMovement.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.action; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.Position; 20 | 21 | public final class AsyncMovement { 22 | 23 | private final Position startPosition; 24 | private final Position endPosition; 25 | private final int startToEndSpeed; 26 | private final int endToStartSpeed; 27 | private final int direction; 28 | 29 | public AsyncMovement(Position startPosition, Position endPosition, int startToEndSpeed, int endToStartSpeed, 30 | int direction) { 31 | this.startPosition = startPosition; 32 | this.endPosition = endPosition; 33 | this.startToEndSpeed = startToEndSpeed; 34 | this.endToStartSpeed = endToStartSpeed; 35 | this.direction = direction; 36 | } 37 | 38 | public Position getStartPosition() { 39 | return startPosition; 40 | } 41 | 42 | public Position getEndPosition() { 43 | return endPosition; 44 | } 45 | 46 | public int getStartToEndSpeed() { 47 | return startToEndSpeed; 48 | } 49 | 50 | public int getEndToStartSpeed() { 51 | return endToStartSpeed; 52 | } 53 | 54 | public int getDirection() { 55 | return direction; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/rs/io/JsonPlayerFileHandler.java: -------------------------------------------------------------------------------- 1 | package com.rs.io; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.cedarsoftware.util.io.JsonReader; 20 | import com.rs.entity.player.PlayerAttributes; 21 | 22 | import java.io.File; 23 | import java.io.FileInputStream; 24 | import java.nio.file.NoSuchFileException; 25 | 26 | /** 27 | * An {@link PlayerFileHandler} implementation for JSON player attribute saving/loading. 28 | * 29 | * @author Pure_ 30 | */ 31 | public final class JsonPlayerFileHandler implements PlayerFileHandler { 32 | 33 | @Override 34 | public void save(PlayerAttributes attributes) throws Exception { 35 | String fileName = getStorageDirectory() + attributes.getUsername() + ".json"; 36 | JsonUtils.write(fileName, true, attributes); 37 | } 38 | 39 | @Override 40 | public PlayerAttributes load(String username) throws Exception { 41 | // Checking if file exists 42 | File file = new File(getStorageDirectory() + username + ".json"); 43 | 44 | if (!file.exists()) { 45 | throw new NoSuchFileException(file.getAbsolutePath()); 46 | } 47 | 48 | // Reading file 49 | JsonReader reader = new JsonReader(new FileInputStream(file)); 50 | PlayerAttributes attributes = (PlayerAttributes) reader.readObject(); 51 | reader.close(); 52 | return attributes; 53 | } 54 | 55 | @Override 56 | public String getStorageDirectory() { 57 | return "./data/characters/"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/rs/Settings.java: -------------------------------------------------------------------------------- 1 | package com.rs; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.cedarsoftware.util.io.JsonReader; 20 | import com.rs.entity.Position; 21 | 22 | import java.io.File; 23 | import java.io.FileInputStream; 24 | import java.io.FileNotFoundException; 25 | 26 | /** 27 | * A collection of server settings, loaded from json. 28 | */ 29 | public final class Settings { 30 | 31 | private String serverName; 32 | private Position startPosition; 33 | private int maxConsPerHost; 34 | private boolean hashPasswords; 35 | private String dateFormat; 36 | 37 | public static Settings load(String fileName) throws Exception { 38 | // Checking if file exists 39 | File file = new File(fileName); 40 | 41 | if (!file.exists()) { 42 | throw new FileNotFoundException("The settings file was not found"); 43 | } 44 | 45 | // Reading file 46 | JsonReader reader = new JsonReader(new FileInputStream(file)); 47 | Settings settings = (Settings) reader.readObject(); 48 | reader.close(); 49 | return settings; 50 | } 51 | 52 | public String getServerName() { 53 | return serverName; 54 | } 55 | 56 | public Position getStartPosition() { 57 | return startPosition; 58 | } 59 | 60 | public int getMaxConsPerHost() { 61 | return maxConsPerHost; 62 | } 63 | 64 | public boolean isHashingPasswords() { 65 | return hashPasswords; 66 | } 67 | 68 | public String getDateFormat() { 69 | return dateFormat; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/rs/task/TaskHandler.java: -------------------------------------------------------------------------------- 1 | package com.rs.task; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Iterator; 23 | 24 | /** 25 | * A simple task handler. 26 | * 27 | * @author Pure_ 28 | */ 29 | public final class TaskHandler { 30 | 31 | /** 32 | * A collection of tasks. 33 | */ 34 | private static final ArrayList tasks = new ArrayList<>(); 35 | 36 | /** 37 | * Performs a processing task on all active tasks. 38 | */ 39 | public static void tick() { 40 | for (Iterator itr = tasks.iterator(); itr.hasNext(); ) { 41 | Task task = itr.next(); 42 | 43 | // Removing completed tasks 44 | if (!task.isActive()) { 45 | itr.remove(); 46 | continue; 47 | } 48 | 49 | // Processing task 50 | try { 51 | task.tick(); 52 | } catch (Exception ex) { 53 | ex.printStackTrace(); 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * Deactivates all tasks which belong to the given player. 60 | */ 61 | public static void remove(Player player) { 62 | for (Task task : tasks) { 63 | if (task.getPlayer().equals(player)) { 64 | task.setInactive(); 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * Adds a task to the execution list. 71 | */ 72 | public static void submit(Task task) { 73 | tasks.add(task); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/rs/net/HostGateway.java: -------------------------------------------------------------------------------- 1 | package com.rs.net; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import java.util.concurrent.ConcurrentHashMap; 20 | 21 | /** 22 | * A static gateway type class that is used to limit the maximum amount of connections per host. 23 | * 24 | * @author blakeman8192 25 | */ 26 | public final class HostGateway { 27 | 28 | /** 29 | * Used to keep track of hosts and their amount of connections. 30 | */ 31 | private static final ConcurrentHashMap map = new ConcurrentHashMap<>(); 32 | 33 | /** 34 | * Checks the host into the gateway. 35 | * 36 | * @param host the host 37 | */ 38 | public static void enter(String host) { 39 | if (map.containsKey(host)) { 40 | map.put(host, map.get(host) + 1); 41 | } else { 42 | map.put(host, 1); 43 | } 44 | } 45 | 46 | /** 47 | * Unchecks the host from the gateway. 48 | * 49 | * @param host the host 50 | */ 51 | public static void exit(String host) { 52 | Integer amount = map.get(host); 53 | 54 | if (amount == null) 55 | return; 56 | 57 | // Remove the host from the map if it's at 1 connection. 58 | if (amount == 1) { 59 | map.remove(host); 60 | return; 61 | } 62 | 63 | // Otherwise decrement the amount of connections stored. 64 | map.put(host, amount - 1); 65 | } 66 | 67 | /** 68 | * @return The number of connections from the given host. 69 | */ 70 | public static int count(String host) { 71 | return map.getOrDefault(host, 0); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/rs/ServerModule.java: -------------------------------------------------------------------------------- 1 | package com.rs; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.google.inject.AbstractModule; 20 | import com.google.inject.Singleton; 21 | import com.google.inject.multibindings.Multibinder; 22 | import com.google.inject.name.Names; 23 | import com.rs.io.JsonPlayerFileHandler; 24 | import com.rs.io.PlayerFileHandler; 25 | import com.rs.service.NetworkService; 26 | import com.rs.service.GameService; 27 | import com.rs.service.Service; 28 | import com.rs.util.AbstractCredentialValidator; 29 | import com.rs.util.LenientCredentialValidator; 30 | 31 | public class ServerModule extends AbstractModule { 32 | 33 | private final String host; 34 | private final int port; 35 | private final int tickRate; 36 | 37 | public ServerModule(String host, int port, int tickRate) { 38 | this.host = host; 39 | this.port = port; 40 | this.tickRate = tickRate; 41 | } 42 | 43 | protected void configure() { 44 | Multibinder serviceBinder = Multibinder.newSetBinder(binder(), Service.class); 45 | serviceBinder.addBinding().to(NetworkService.class).in(Singleton.class); 46 | serviceBinder.addBinding().to(GameService.class).in(Singleton.class); 47 | bind(String.class).annotatedWith(Names.named("host")).toInstance(host); 48 | bind(Integer.class).annotatedWith(Names.named("port")).toInstance(port); 49 | bind(Integer.class).annotatedWith(Names.named("tickRate")).toInstance(tickRate); 50 | 51 | bind(PlayerFileHandler.class).to(JsonPlayerFileHandler.class).in(Singleton.class); 52 | bind(AbstractCredentialValidator.class).to(LenientCredentialValidator.class).in(Singleton.class); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/rs/util/LenientCredentialValidator.java: -------------------------------------------------------------------------------- 1 | package com.rs.util; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | /** 20 | * A lenient credential validator, which enforces a character whitelist, as well as length 21 | * limits on both username and passwords. 22 | */ 23 | public class LenientCredentialValidator extends AbstractCredentialValidator { 24 | 25 | private static final char[] VALID_PASSWORD_CHARACTERS = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 26 | + "0123456789!\"\243$%^&*()-_=+[{]};:'@#~,<.>/?\\| ").toCharArray(); 27 | private static final char[] VALID_USERNAME_CHARACTERS = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 28 | + "0123456789! ").toCharArray(); 29 | 30 | public boolean validateUsername(String username) { 31 | // Length check 32 | if (username.length() == 0 || username.length() > 12) { 33 | return false; 34 | } 35 | 36 | // Checking each character 37 | for (int i = 0; i < username.length(); i++) { 38 | if (!Misc.contains(VALID_USERNAME_CHARACTERS, username.charAt(i))) { 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | 45 | public boolean validatePassword(String password) { 46 | // Length check 47 | if (password.length() == 0 || password.length() > 20) { 48 | return false; 49 | } 50 | 51 | // Checking each character 52 | for (int i = 0; i < password.length(); i++) { 53 | if (!Misc.contains(VALID_PASSWORD_CHARACTERS, password.charAt(i))) { 54 | return false; 55 | } 56 | } 57 | return true; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/player/infractions/PlayerInfractions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | package com.rs.entity.player.infractions; 18 | 19 | import com.rs.Server; 20 | 21 | import java.text.DateFormat; 22 | import java.text.SimpleDateFormat; 23 | import java.util.Date; 24 | 25 | /** 26 | * The infractions associated with a given player, this does not support any IP-based infractions. 27 | */ 28 | public final class PlayerInfractions { 29 | 30 | private static final DateFormat DATE_FORMAT = new SimpleDateFormat(Server.getInstance().getSettings().getDateFormat()); 31 | private boolean banned; 32 | private boolean muted; 33 | private Date banExpirationDate; 34 | private Date muteExpirationDate; 35 | 36 | public void setBanned(boolean banned) { 37 | this.banned = banned; 38 | } 39 | 40 | public void setMuted(boolean muted) { 41 | this.muted = muted; 42 | } 43 | 44 | public void setBanExpirationDate(Date banExpirationDate) { 45 | this.banExpirationDate = banExpirationDate; 46 | } 47 | 48 | public void setMuteExpirationDate(Date muteExpirationDate) { 49 | this.muteExpirationDate = muteExpirationDate; 50 | } 51 | 52 | public boolean isBanned() { 53 | return banned && (banExpirationDate == null || new Date().before(banExpirationDate)); 54 | } 55 | 56 | public boolean isMuted() { 57 | return muted && (muteExpirationDate == null || new Date().before(muteExpirationDate)); 58 | } 59 | 60 | public String banExpiration() { 61 | return banExpirationDate != null ? DATE_FORMAT.format(banExpirationDate) : "never"; 62 | } 63 | 64 | public String muteExpiration() { 65 | return muteExpirationDate != null ? DATE_FORMAT.format(muteExpirationDate) : "never"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /plugins/scripts/ReportAbuse.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | import com.rs.Server 18 | import com.rs.entity.player.Player 19 | import com.rs.entity.player.infractions.ReportAbuseRule 20 | import com.rs.plugin.PluginHandler 21 | import com.rs.plugin.event.ReportAbuseEvent 22 | import com.rs.plugin.listener.ReportAbuseListener 23 | 24 | import java.text.DateFormat 25 | import java.text.SimpleDateFormat 26 | 27 | class ReportAbuse implements ReportAbuseListener { 28 | 29 | File logFile = new File("./data/logs/report-abuse.log") 30 | DateFormat dateFormat = new SimpleDateFormat(Server.getInstance().getSettings().getDateFormat()) 31 | 32 | void reportAbuse(ReportAbuseEvent e) { 33 | Player player = e.getPlayer() 34 | com.rs.entity.player.infractions.ReportAbuse abuse = e.getReportAbuse() 35 | boolean muted = abuse.muteFor48Hours 36 | 37 | // Mute using command - should remove this once we allow plugins to interact with each other 38 | if (muted) { 39 | Calendar expiration = Calendar.getInstance() 40 | expiration.setTime(new Date()) 41 | expiration.add(Calendar.HOUR_OF_DAY, 48) 42 | PluginHandler.dispatchCommand(e.getPlayer(), "mute", dateFormat.format(expiration.getTime())) 43 | } 44 | 45 | log(player.getAttributes().getUsername(), abuse.username, abuse.rule, muted) 46 | player.setCurrentInterfaceId(-1) 47 | } 48 | 49 | void log(String author, String target, ReportAbuseRule rule, boolean muted) { 50 | String appDate = dateFormat.format(new Date()) 51 | String log = "[$appDate]: [$author -> $target] $rule muted=$muted" 52 | 53 | FileWriter writer = new FileWriter(logFile, true) 54 | writer.write(log + System.lineSeparator()) 55 | writer.flush() 56 | writer.close() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RuneSource 2 | ======================= 3 | 4 | ### What is RuneSource? 5 | RuneSource is an [open-source](http://www.opensource.org/) server for the popular game [RuneScape](https://runescape.com), 6 | written in the Java programming language. 7 | It is important to note that RuneSource is in no way endorsed or affiliated with RuneScape or its creator company, 8 | Jagex. Use of RuneSource is also against the RuneScape Terms of Service (which you may be bound by). 9 | RuneSource is licensed via the GNU General Public License Version 3. 10 | Please note that this server is not ready for production - it has minimal content implemented. 11 | 12 | ### Why was RuneSource made? 13 | RuneSource was made by [blakeman8192](https://github.com/blakeman8192) for the general RuneScape emulation community, 14 | which is spread across many internet forums and IRC channels. 15 | The purpose of RuneSource is to provide a stable, high-performance, and simple RuneScape server emulator for people to 16 | use. 17 | At the current time of release (late 2010), all existing RuneScape server emulators are either very unstable and slow, 18 | or very complicated and hard to work with. 19 | RuneSource aims to solve these problems by being a stable and efficient server, and above all, by being simple and 20 | easy to use. 21 | 22 | ### New Features 23 | * Powerful Plugins using Groovy 24 | * Task System 25 | * Full Player Support 26 | * Full Npc Support 27 | * Friends List and Private Messaging (with privacy settings) 28 | * JSON Data Serialization (optional: passwords are hashed using SHA-256) 29 | * Banning and muting with expiration dates 30 | * Misc stuff 31 | * Validate equipped items 32 | * Emotes 33 | * Running 34 | * Correct weapon interfaces 35 | * Persistent settings 36 | * Buffer caching 37 | * Weapon stack merging (i.e. if you have 10 arrows equipped, and equip 5 more they combine into 15) 38 | * Login attempt throttling 39 | * Report abuse interface 40 | * Saves all players on shutdown (e.g. if you use Ctrl+C) 41 | * [More planned...](https://github.com/PureCS/runesource/issues) 42 | 43 | ### Configuration 44 | You can configure your RuneSource by reading the [use guide](USEGUIDE.md). 45 | 46 | ### Copyright 47 | Copyright (c) 2010 [Blake Beaupain](https://github.com/blakeman8192) 48 | Copyright (c) 2015-2018 [PureCS](https://github.com/purecs) 49 | RuneScape is copyright and a registered trademark of Jagex Ltd. 50 | RuneSource and its authors are in no way affiliated with Jagex or RuneScape. 51 | RuneSource exists solely for educational purposes, and is licensed via GPLv3. 52 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/npc/NpcUpdateContext.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.npc; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.EntityUpdateContext; 20 | import com.rs.net.StreamBuffer; 21 | 22 | /** 23 | * The update flags and buffer caches for a {@link Npc}. 24 | */ 25 | public final class NpcUpdateContext extends EntityUpdateContext { 26 | 27 | private final StreamBuffer.BufferCache bufferCache = new StreamBuffer.BufferCache(); 28 | private boolean npcDefinitionUpdateRequired = false; 29 | 30 | public int mask() { 31 | int mask = 0x0; 32 | 33 | if (isAnimationUpdateRequired()) { 34 | mask |= 0x10; 35 | } 36 | 37 | if (isPrimaryHitUpdateRequired()) { 38 | mask |= 0x40; 39 | } 40 | 41 | if (isGraphicsUpdateRequired()) { 42 | mask |= 0x80; 43 | } 44 | 45 | if (isInteractingNpcUpdateRequired()) { 46 | mask |= 0x20; 47 | } 48 | 49 | if (isForcedChatUpdateRequired()) { 50 | mask |= 0x1; 51 | } 52 | 53 | if (isSecondaryHitUpdateRequired()) { 54 | mask |= 0x8; 55 | } 56 | 57 | if (isNpcDefinitionUpdateRequired()) { 58 | mask |= 0x2; 59 | } 60 | 61 | if (isFaceCoordinatesUpdateRequired()) { 62 | mask |= 0x4; 63 | } 64 | return mask; 65 | } 66 | 67 | public void setUpdateRequired() { 68 | super.setUpdateRequired(); 69 | bufferCache.setOutdated(); 70 | } 71 | 72 | public void setNpcDefintionUpdateRequired() { 73 | setUpdateRequired(); 74 | npcDefinitionUpdateRequired = true; 75 | } 76 | 77 | public void resetFlags() { 78 | super.resetFlags(); 79 | bufferCache.setOutdated(); 80 | } 81 | 82 | public boolean isNpcDefinitionUpdateRequired() { 83 | return npcDefinitionUpdateRequired; 84 | } 85 | 86 | public StreamBuffer.BufferCache getBufferCache() { 87 | return bufferCache; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/Entity.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity; 2 | 3 | import com.rs.util.Tickable; 4 | 5 | /** 6 | * An in-game entity. 7 | */ 8 | public abstract class Entity implements Tickable { 9 | 10 | private final MovementHandler movementHandler = new MovementHandler(this); 11 | private final EntityUpdateContext updateContext; 12 | private Position currentRegion = new Position(0, 0, 0); 13 | private int slot = -1; 14 | private int primaryDirection = -1; 15 | private int secondaryDirection = -1; 16 | private boolean needsPlacement = false; 17 | private boolean resetMovementQueue = false; 18 | 19 | public Entity(EntityUpdateContext updateContext) { 20 | this.updateContext = updateContext; 21 | } 22 | 23 | public abstract Position getPosition(); 24 | 25 | /** 26 | * Resets the entity after updating. 27 | */ 28 | public void reset() { 29 | setPrimaryDirection(-1); 30 | setSecondaryDirection(-1); 31 | setResetMovementQueue(false); 32 | setNeedsPlacement(false); 33 | updateContext.resetFlags(); 34 | } 35 | 36 | public MovementHandler getMovementHandler() { 37 | return movementHandler; 38 | } 39 | 40 | public int getPrimaryDirection() { 41 | return primaryDirection; 42 | } 43 | 44 | /** 45 | * Sets the player's primary movement direction. 46 | */ 47 | public void setPrimaryDirection(int primaryDirection) { 48 | this.primaryDirection = primaryDirection; 49 | } 50 | 51 | public int getSecondaryDirection() { 52 | return secondaryDirection; 53 | } 54 | 55 | /** 56 | * Sets the player's secondary movement direction. 57 | */ 58 | public void setSecondaryDirection(int secondaryDirection) { 59 | this.secondaryDirection = secondaryDirection; 60 | } 61 | 62 | /** 63 | * Gets the current region. 64 | */ 65 | public Position getCurrentRegion() { 66 | return currentRegion; 67 | } 68 | 69 | public void setCurrentRegion(Position currentRegion) { 70 | this.currentRegion = currentRegion; 71 | } 72 | 73 | public boolean isResetMovementQueue() { 74 | return resetMovementQueue; 75 | } 76 | 77 | public void setResetMovementQueue(boolean resetMovementQueue) { 78 | this.resetMovementQueue = resetMovementQueue; 79 | } 80 | 81 | public void setNeedsPlacement(boolean needsPlacement) { 82 | this.needsPlacement = needsPlacement; 83 | } 84 | 85 | /** 86 | * Gets whether or not the player needs to be placed. 87 | */ 88 | public boolean needsPlacement() { 89 | return needsPlacement; 90 | } 91 | 92 | /** 93 | * Gets the player slot. 94 | */ 95 | public int getSlot() { 96 | return slot; 97 | } 98 | 99 | public void setSlot(int slot) { 100 | this.slot = slot; 101 | } 102 | 103 | public EntityUpdateContext getUpdateContext() { 104 | return updateContext; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/rs/util/WeaponDefinition.java: -------------------------------------------------------------------------------- 1 | package com.rs.util; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | /** 20 | * A weapon's definition. 21 | */ 22 | public final class WeaponDefinition implements Comparable { 23 | 24 | private final int id; 25 | private final String name; 26 | private final Type type; 27 | 28 | public WeaponDefinition(int id, String name, Type type) { 29 | this.id = id; 30 | this.name = name; 31 | this.type = type; 32 | } 33 | 34 | public int getId() { 35 | return id; 36 | } 37 | 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | public Type getType() { 43 | return type; 44 | } 45 | 46 | @Override 47 | public int compareTo(WeaponDefinition o) { 48 | return id - o.id; 49 | } 50 | 51 | public enum Type { 52 | UNARMED(5855, 5857, -1), 53 | STAFF(328, 331, 329), 54 | WARHAMMER(425, 428, 426), 55 | SYTHE(776, 779, 777), 56 | BATTLE_AXE(1698, 1701, 1699), 57 | BOW(1764, 1767, 1765), 58 | CROSSBOW(1749, 1752, 1750), 59 | SWORD(2423, 2426, 2424), 60 | FLOWERS(2423, 2426, 2424), 61 | DAGGER(2276, 2279, 2277), 62 | MACE(3796, 3799, 3797), 63 | KNIFE(4446, 4449, 4447), 64 | DART(4446, 4449, 4447), 65 | JAVELIN(4446, 4449, 4447), 66 | THROWNAXE(4446, 4449, 4447), 67 | SPEAR(4679, 4682, 4680), 68 | SWORD_2H(4705, 4708, 4706), 69 | LONGSWORD(4705, 4708, 4706), 70 | PICKAXE(5570, 5573, 5571), 71 | CLAW(7762, 7765, 7763), 72 | HALBERD(8460, 8463, 8461), 73 | WHIP(12290, 12293, 12291), 74 | UNKNOWN(-1, -1, -1); 75 | 76 | private final int interfaceId; 77 | private final int weaponNameId; 78 | private final int weaponImageId; 79 | 80 | Type(int interfaceId, int weaponNameId, int weaponImageId) { 81 | this.interfaceId = interfaceId; 82 | this.weaponNameId = weaponNameId; 83 | this.weaponImageId = weaponImageId; 84 | } 85 | 86 | public int getInterfaceId() { 87 | return interfaceId; 88 | } 89 | 90 | public int getWeaponNameId() { 91 | return weaponNameId; 92 | } 93 | 94 | public int getWeaponImageId() { 95 | return weaponImageId; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/rs/task/Task.java: -------------------------------------------------------------------------------- 1 | package com.rs.task; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.util.Tickable; 20 | import com.rs.entity.player.Player; 21 | 22 | /** 23 | * A simple task base class. 24 | * 25 | * @author Pure_ 26 | */ 27 | public abstract class Task implements Tickable { 28 | 29 | private boolean active; 30 | private int currentDelay; 31 | private final int delay; 32 | private final boolean runOnce; 33 | private final Player player; 34 | private final Object[] args; 35 | private int ticks; 36 | 37 | /** 38 | * Creates a new task instance which will run once with no arguments. 39 | */ 40 | public Task(int delay, Player player) { 41 | this(delay, true, player); 42 | } 43 | 44 | /** 45 | * Creates a new task instance. 46 | * 47 | * @param delay the delay (in server ticks) until the task is to be executed 48 | * @param runOnce if the task should only be executed once 49 | * @param player the player this task is bound to 50 | * @param args the args to provide to the instance 51 | */ 52 | public Task(int delay, boolean runOnce, Player player, Object... args) { 53 | this.delay = delay; 54 | this.runOnce = runOnce; 55 | this.player = player; 56 | this.args = args; 57 | this.active = true; 58 | currentDelay = delay; 59 | } 60 | 61 | /** 62 | * Contains the task's executable code, this is executed once the delay is depleted. 63 | */ 64 | protected abstract void process(); 65 | 66 | /** 67 | * Performs a logic tick, this checks if the task can be executed yet. 68 | */ 69 | public void tick() throws Exception { 70 | if (active && currentDelay-- <= 0) { 71 | process(); 72 | ticks++; 73 | currentDelay = delay; 74 | 75 | if (runOnce) { 76 | active = false; 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * Whether or not the task should be removed next tick. 83 | */ 84 | public boolean isActive() { 85 | return active; 86 | } 87 | 88 | /** 89 | * Sets the task as inactive. 90 | */ 91 | public void setInactive() { 92 | this.active = false; 93 | } 94 | 95 | /** 96 | * Gets the array of arguments passed to the task upon creation. 97 | */ 98 | public Object[] getArgs() { 99 | return args; 100 | } 101 | 102 | /** 103 | * The player this task is bound to. 104 | */ 105 | public Player getPlayer() { 106 | return player; 107 | } 108 | 109 | /** 110 | * The amount of times this task has been executed. 111 | */ 112 | public int getTicks() { 113 | return ticks; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/player/PlayerSettings.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.player; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | /** 20 | * A {@link Player}'s in-game settings. 21 | */ 22 | public final class PlayerSettings { 23 | 24 | private MouseButtons mouseButtons = MouseButtons.TWO; 25 | private Brightness brightness = Brightness.NORMAL; 26 | private boolean chatEffects = true; 27 | private boolean splitPrivateChat = false; 28 | private boolean acceptAid = false; 29 | private boolean runToggled = false; 30 | private boolean autoRetaliate = true; 31 | private int publicChatMode = 0; 32 | private int privateChatMode = 0; 33 | private int tradeMode = 0; 34 | 35 | public MouseButtons getMouseButtons() { 36 | return mouseButtons; 37 | } 38 | 39 | public void setMouseButtons(MouseButtons mouseButtons) { 40 | this.mouseButtons = mouseButtons; 41 | } 42 | 43 | public Brightness getBrightness() { 44 | return brightness; 45 | } 46 | 47 | public void setBrightness(Brightness brightness) { 48 | this.brightness = brightness; 49 | } 50 | 51 | public boolean isChatEffects() { 52 | return chatEffects; 53 | } 54 | 55 | public void setChatEffects(boolean chatEffects) { 56 | this.chatEffects = chatEffects; 57 | } 58 | 59 | public boolean isSplitPrivateChat() { 60 | return splitPrivateChat; 61 | } 62 | 63 | public void setSplitPrivateChat(boolean splitPrivateChat) { 64 | this.splitPrivateChat = splitPrivateChat; 65 | } 66 | 67 | public boolean isAcceptAid() { 68 | return acceptAid; 69 | } 70 | 71 | public void setAcceptAid(boolean acceptAid) { 72 | this.acceptAid = acceptAid; 73 | } 74 | 75 | public boolean isRunToggled() { 76 | return runToggled; 77 | } 78 | 79 | public void setRunToggled(boolean runToggled) { 80 | this.runToggled = runToggled; 81 | } 82 | 83 | public boolean isAutoRetaliate() { 84 | return autoRetaliate; 85 | } 86 | 87 | public void setAutoRetaliate(boolean autoRetaliate) { 88 | this.autoRetaliate = autoRetaliate; 89 | } 90 | 91 | public int getPublicChatMode() { 92 | return publicChatMode; 93 | } 94 | 95 | public void setPublicChatMode(int publicChatMode) { 96 | this.publicChatMode = publicChatMode; 97 | } 98 | 99 | public int getPrivateChatMode() { 100 | return privateChatMode; 101 | } 102 | 103 | public void setPrivateChatMode(int privateChatMode) { 104 | this.privateChatMode = privateChatMode; 105 | } 106 | 107 | public int getTradeMode() { 108 | return tradeMode; 109 | } 110 | 111 | public void setTradeMode(int tradeMode) { 112 | this.tradeMode = tradeMode; 113 | } 114 | 115 | public enum MouseButtons { 116 | ONE, TWO 117 | } 118 | 119 | public enum Brightness { 120 | DARK, NORMAL, BRIGHT, VERY_BRIGHT; 121 | 122 | public int settingValue() { 123 | return ordinal() + 1; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/EntityUpdateContext.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | /** 20 | * The update flags for an {@link com.rs.entity.Entity}. 21 | */ 22 | public abstract class EntityUpdateContext { 23 | 24 | private boolean updateRequired = false; 25 | private boolean graphicsUpdateRequired = false; 26 | private boolean animationUpdateRequired = false; 27 | private boolean forcedChatUpdateRequired = false; 28 | private boolean faceCoordinatesUpdateRequired = false; 29 | private boolean primaryHitUpdateRequired = false; 30 | private boolean secondaryHitUpdateRequired = false; 31 | private boolean interactingNpcUpdateRequired = false; 32 | 33 | public abstract int mask(); 34 | 35 | public void setUpdateRequired() { 36 | updateRequired = true; 37 | } 38 | 39 | public void setGraphicsUpdateRequired() { 40 | setUpdateRequired(); 41 | graphicsUpdateRequired = true; 42 | } 43 | 44 | public void setAnimationUpdateRequired() { 45 | setUpdateRequired(); 46 | animationUpdateRequired = true; 47 | } 48 | 49 | public void setForcedChatUpdateRequired() { 50 | setUpdateRequired(); 51 | forcedChatUpdateRequired = true; 52 | } 53 | 54 | public void setInteractingNpcUpdateRequired() { 55 | setUpdateRequired(); 56 | interactingNpcUpdateRequired = true; 57 | } 58 | 59 | public void setFaceCoordinatesUpdateRequired() { 60 | setUpdateRequired(); 61 | faceCoordinatesUpdateRequired = true; 62 | } 63 | 64 | public void setPrimaryHitUpdateRequired() { 65 | setUpdateRequired(); 66 | primaryHitUpdateRequired = true; 67 | } 68 | 69 | public void setSecondaryHitUpdateRequired() { 70 | setUpdateRequired(); 71 | secondaryHitUpdateRequired = true; 72 | } 73 | 74 | public boolean isUpdateRequired() { 75 | return updateRequired; 76 | } 77 | 78 | public boolean isGraphicsUpdateRequired() { 79 | return graphicsUpdateRequired; 80 | } 81 | 82 | public boolean isAnimationUpdateRequired() { 83 | return animationUpdateRequired; 84 | } 85 | 86 | public boolean isForcedChatUpdateRequired() { 87 | return forcedChatUpdateRequired; 88 | } 89 | 90 | public boolean isInteractingNpcUpdateRequired() { 91 | return interactingNpcUpdateRequired; 92 | } 93 | 94 | public boolean isFaceCoordinatesUpdateRequired() { 95 | return faceCoordinatesUpdateRequired; 96 | } 97 | 98 | public boolean isPrimaryHitUpdateRequired() { 99 | return primaryHitUpdateRequired; 100 | } 101 | 102 | public boolean isSecondaryHitUpdateRequired() { 103 | return secondaryHitUpdateRequired; 104 | } 105 | 106 | /** 107 | * Resets all update flags. 108 | */ 109 | public void resetFlags() { 110 | updateRequired = false; 111 | graphicsUpdateRequired = false; 112 | animationUpdateRequired = false; 113 | forcedChatUpdateRequired = false; 114 | interactingNpcUpdateRequired = false; 115 | faceCoordinatesUpdateRequired = false; 116 | primaryHitUpdateRequired = false; 117 | secondaryHitUpdateRequired = false; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /plugins/scripts/Emotes.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | import com.rs.entity.player.Player 18 | import com.rs.plugin.event.ActionButtonEvent 19 | import com.rs.plugin.listener.ActionButtonListener 20 | 21 | class Emotes implements ActionButtonListener { 22 | 23 | void actionButton(ActionButtonEvent evt) { 24 | Player player = evt.getPlayer() 25 | 26 | switch (evt.getActionButtonId()) { 27 | case 168: // Yes 28 | player.startAnimation 855 29 | break 30 | case 169: // No 31 | player.startAnimation 856 32 | break 33 | case 162: // Think 34 | player.startAnimation 857 35 | break 36 | case 164: // Bow 37 | player.startAnimation 858 38 | break 39 | case 165: // Angry 40 | player.startAnimation 859 41 | break 42 | case 161: // Cry 43 | player.startAnimation 860 44 | break 45 | case 170: // Laugh 46 | player.startAnimation 861 47 | break 48 | case 171: // Cheer 49 | player.startAnimation 862 50 | break 51 | case 163: // Wave 52 | player.startAnimation 863 53 | break 54 | case 167: // Beckon 55 | player.startAnimation 864 56 | break 57 | case 172: // Clap 58 | player.startAnimation 865 59 | break 60 | case 166: // Dance 61 | player.startAnimation 866 62 | break 63 | case 52050: // Panic 64 | player.startAnimation 2105 65 | break 66 | case 52051: // Jig 67 | player.startAnimation 2106 68 | break 69 | case 52052: // Spin 70 | player.startAnimation 2107 71 | break 72 | case 52053: // Head Bang 73 | player.startAnimation 2108 74 | break 75 | case 52054: // Joy Jump 76 | player.startAnimation 2109 77 | break 78 | case 52055: // Rasp'berry 79 | player.startAnimation 2110 80 | break 81 | case 52056: // Yawn 82 | player.startAnimation 2111 83 | break 84 | case 52057: // Salute 85 | player.startAnimation 2112 86 | break 87 | case 52058: // Shrug 88 | player.startAnimation 2113 89 | break 90 | case 43092: // Blow Kiss 91 | player.startAnimation 1368 92 | break 93 | case 2155: // Glass Box 94 | player.startAnimation 1131 95 | break 96 | case 2154: // Glass Wall 97 | player.startAnimation 1128 98 | break 99 | case 25103: // Climb Rope 100 | player.startAnimation 1130 101 | break 102 | case 25106: // Lean 103 | player.startAnimation 1129 104 | break 105 | case 52071: // Goblin Bow 106 | player.startAnimation 2127 107 | break 108 | case 52072: // Goblin Dance 109 | player.startAnimation 2128 110 | break 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/test/java/com/rs/entity/player/skills/SkillsTest.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.player.skills; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import org.junit.jupiter.api.BeforeEach; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertEquals; 26 | 27 | final class SkillsTest { 28 | 29 | private Skills skills; 30 | 31 | @BeforeEach 32 | void setUp() { 33 | skills = new Skills(); 34 | } 35 | 36 | @Test 37 | void testLevels_NewAccount() { 38 | assertEquals(3, skills.getCombatLevel()); 39 | assertEquals(30, skills.getTotalLevel()); 40 | } 41 | 42 | @Test 43 | void testLevels_MaxedAccount() { 44 | List skillList = new ArrayList<>(); 45 | 46 | for (SkillType type : SkillType.values()) { 47 | skillList.add(new Skill(type, 99, 99, 13034431)); 48 | } 49 | 50 | Skills skills = new Skills(skillList); 51 | assertEquals(126, skills.getCombatLevel()); 52 | assertEquals(2079, skills.getTotalLevel()); 53 | } 54 | 55 | @Test 56 | void testAddExperience() { 57 | skills.addExperience(SkillType.ATTACK, 50); 58 | assertEquals(1, skills.level(SkillType.ATTACK)); 59 | assertEquals(1, skills.maxLevel(SkillType.ATTACK)); 60 | assertEquals(30, skills.getTotalLevel()); 61 | } 62 | 63 | @Test 64 | void testAddExperience_LevelUp_OneLevel() { 65 | skills.addExperience(SkillType.ATTACK, 83); 66 | assertEquals(2, skills.level(SkillType.ATTACK)); 67 | assertEquals(2, skills.maxLevel(SkillType.ATTACK)); 68 | assertEquals(31, skills.getTotalLevel()); 69 | } 70 | 71 | @Test 72 | void testAddExperience_LevelUp_TwoLevels() { 73 | skills.addExperience(SkillType.ATTACK, 174); 74 | assertEquals(3, skills.level(SkillType.ATTACK)); 75 | assertEquals(3, skills.maxLevel(SkillType.ATTACK)); 76 | assertEquals(32, skills.getTotalLevel()); 77 | } 78 | 79 | @Test 80 | void testCombatLevel() { 81 | assertEquals(3, Skills.combatLevel(1, 1, 1, 1, 1, 10, 1)); 82 | assertEquals(126, Skills.combatLevel(99, 99, 99, 99, 99, 99, 99)); 83 | } 84 | 85 | @Test 86 | void testLevelForExp() { 87 | assertEquals(1, Skills.levelForExp(0)); 88 | assertEquals(2, Skills.levelForExp(83)); 89 | assertEquals(99, Skills.levelForExp(13034431)); 90 | assertEquals(99, Skills.levelForExp(20000000)); 91 | } 92 | 93 | @Test 94 | void testAddExperience_RemoveExperience() { 95 | assertEquals(3, skills.getCombatLevel()); 96 | assertEquals(30, skills.getTotalLevel()); 97 | 98 | for (SkillType type : SkillType.values()) { 99 | skills.addExperience(type, 13034431); 100 | } 101 | assertEquals(126, skills.getCombatLevel()); 102 | assertEquals(2079, skills.getTotalLevel()); 103 | 104 | for (SkillType type : SkillType.values()) { 105 | skills.addExperience(type, -13034431); 106 | } 107 | assertEquals(3, skills.getCombatLevel()); 108 | assertEquals(30, skills.getTotalLevel()); 109 | } 110 | 111 | @Test 112 | void testNormalizeExp() { 113 | assertEquals(0, Skills.normalizeExp(-1)); 114 | assertEquals(0, Skills.normalizeExp(0)); 115 | assertEquals(100_000, Skills.normalizeExp(100_000)); 116 | assertEquals(200_000_000, Skills.normalizeExp(200_000_000)); 117 | assertEquals(200_000_000, Skills.normalizeExp(200_000_001)); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/npc/Npc.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.npc; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.Entity; 20 | import com.rs.entity.Position; 21 | import com.rs.entity.action.*; 22 | 23 | /** 24 | * A Non-Player Character. 25 | * 26 | * @author blakeman8192 27 | */ 28 | public final class Npc extends Entity { 29 | 30 | private final int id; 31 | private boolean visible = true; 32 | private Position position = new Position(0, 0); 33 | // Various npc update data. 34 | private String forceChatText; 35 | private Animation animation; 36 | private Graphics graphics; 37 | private Hit primaryHit; 38 | private Hit secondaryHit; 39 | private Position facingPosition; 40 | private Npc interactingNpc; 41 | private int npcDefinitionId; 42 | 43 | /** 44 | * Creates a new Npc. 45 | * 46 | * @param id the NPC ID 47 | */ 48 | public Npc(int id) { 49 | super(new NpcUpdateContext()); 50 | this.id = id; 51 | } 52 | 53 | @Override 54 | public void tick() { 55 | getMovementHandler().tick(); 56 | } 57 | 58 | public void reset() { 59 | super.reset(); 60 | } 61 | 62 | /** 63 | * Gets the NPC ID. 64 | */ 65 | public int getId() { 66 | return id; 67 | } 68 | 69 | public boolean isVisible() { 70 | return visible; 71 | } 72 | 73 | public void setVisible(boolean visible) { 74 | this.visible = visible; 75 | } 76 | 77 | @Override 78 | public Position getPosition() { 79 | return position; 80 | } 81 | 82 | public NpcUpdateContext getUpdateContext() { 83 | return (NpcUpdateContext)super.getUpdateContext(); 84 | } 85 | 86 | public String getForceChatText() { 87 | return forceChatText; 88 | } 89 | 90 | public void setForceChatText(String forceChatText) { 91 | this.forceChatText = forceChatText; 92 | } 93 | 94 | public Animation getAnimation() { 95 | return animation; 96 | } 97 | 98 | public void setAnimation(Animation animation) { 99 | this.animation = animation; 100 | } 101 | 102 | public Graphics getGraphics() { 103 | return graphics; 104 | } 105 | 106 | public void setGraphics(Graphics graphics) { 107 | this.graphics = graphics; 108 | } 109 | 110 | public Hit getPrimaryHit() { 111 | return primaryHit; 112 | } 113 | 114 | public void setPrimaryHit(Hit primaryHit) { 115 | this.primaryHit = primaryHit; 116 | } 117 | 118 | public Hit getSecondaryHit() { 119 | return secondaryHit; 120 | } 121 | 122 | public void setSecondaryHit(Hit secondaryHit) { 123 | this.secondaryHit = secondaryHit; 124 | } 125 | 126 | public Position getFacingPosition() { 127 | return facingPosition; 128 | } 129 | 130 | public void setFacingPosition(Position facingPosition) { 131 | this.facingPosition = facingPosition; 132 | } 133 | 134 | public Npc getInteractingNpc() { 135 | return interactingNpc; 136 | } 137 | 138 | public void setInteractingNpc(Npc interactingNpc) { 139 | this.interactingNpc = interactingNpc; 140 | } 141 | 142 | public int getNpcDefinitionId() { 143 | return npcDefinitionId; 144 | } 145 | 146 | public void setNpcDefinitionId(int npcDefinitionId) { 147 | this.npcDefinitionId = npcDefinitionId; 148 | } 149 | 150 | public int getCurrentHealth() { 151 | return 0; 152 | } 153 | 154 | public int getMaximumHealth() { 155 | return 0; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /plugins/scripts/PlayerSettings.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | import com.rs.entity.player.Player 18 | import com.rs.plugin.event.ActionButtonEvent 19 | import com.rs.plugin.event.PlayerLoggedInEvent 20 | import com.rs.plugin.event.PlayerLoggedOutEvent 21 | import com.rs.plugin.listener.ActionButtonListener 22 | import com.rs.plugin.listener.PlayerConnectivityListener 23 | 24 | class PlayerSettings implements PlayerConnectivityListener, ActionButtonListener { 25 | 26 | void logIn(PlayerLoggedInEvent evt) throws Exception { 27 | Player player = evt.getPlayer() 28 | com.rs.entity.player.PlayerSettings settings = player.getAttributes().getSettings() 29 | 30 | // Send initial setting states 31 | player.sendClientSetting(166, settings.getBrightness().settingValue()) 32 | sendBooleanSetting(player, 170, settings.getMouseButtons() == com.rs.entity.player.PlayerSettings.MouseButtons.ONE) 33 | sendBooleanSetting(player, 171, settings.isChatEffects()) 34 | sendBooleanSetting(player, 172, settings.isAutoRetaliate()) 35 | sendBooleanSetting(player, 173, settings.isRunToggled()) 36 | sendBooleanSetting(player, 287, settings.isSplitPrivateChat()) 37 | sendBooleanSetting(player, 427, settings.isAcceptAid()) 38 | 39 | // Send chat mode 40 | player.sendChatModes() 41 | } 42 | 43 | void logOut(PlayerLoggedOutEvent evt) { 44 | } 45 | 46 | void sendBooleanSetting(Player player, int settingId, boolean condition) { 47 | player.sendClientSetting(settingId, condition ? 1 : 0) 48 | } 49 | 50 | void actionButton(ActionButtonEvent evt) { 51 | Player player = evt.getPlayer() 52 | com.rs.entity.player.PlayerSettings settings = player.getAttributes().getSettings() 53 | 54 | switch (evt.getActionButtonId()) { 55 | case 153: 56 | settings.setRunToggled true 57 | break 58 | case 152: 59 | settings.setRunToggled false 60 | break 61 | case 150: 62 | settings.setAutoRetaliate true 63 | break 64 | case 151: 65 | settings.setAutoRetaliate false 66 | break 67 | case 21076: 68 | settings.setBrightness com.rs.entity.player.PlayerSettings.Brightness.DARK 69 | break 70 | case 24129: 71 | settings.setBrightness com.rs.entity.player.PlayerSettings.Brightness.NORMAL 72 | break 73 | case 24131: 74 | settings.setBrightness com.rs.entity.player.PlayerSettings.Brightness.BRIGHT 75 | break 76 | case 24133: 77 | settings.setBrightness com.rs.entity.player.PlayerSettings.Brightness.VERY_BRIGHT 78 | break 79 | case 24134: 80 | settings.setMouseButtons com.rs.entity.player.PlayerSettings.MouseButtons.TWO 81 | break 82 | case 24135: 83 | settings.setMouseButtons com.rs.entity.player.PlayerSettings.MouseButtons.ONE 84 | break 85 | case 24136: 86 | settings.setChatEffects true 87 | break 88 | case 24137: 89 | settings.setChatEffects false 90 | break 91 | case 3184: 92 | settings.setSplitPrivateChat true 93 | break 94 | case 3185: 95 | settings.setSplitPrivateChat false 96 | break 97 | case 49047: 98 | settings.setAcceptAid true 99 | break 100 | case 49046: 101 | settings.setAcceptAid false 102 | break 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/rs/service/NetworkService.java: -------------------------------------------------------------------------------- 1 | package com.rs.service; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.Server; 20 | import com.rs.entity.player.Client; 21 | import com.rs.entity.player.Player; 22 | import com.rs.net.ConnectionThrottle; 23 | import com.rs.net.HostGateway; 24 | 25 | import java.io.IOException; 26 | import java.nio.channels.SelectionKey; 27 | import java.nio.channels.Selector; 28 | import java.nio.channels.ServerSocketChannel; 29 | import java.nio.channels.SocketChannel; 30 | import java.util.HashMap; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.stream.Collectors; 34 | 35 | /** 36 | * A service which accepts connections, and handles data reading. 37 | */ 38 | public class NetworkService implements Service { 39 | 40 | private ServerSocketChannel serverChannel; 41 | private Map clientMap; 42 | private Selector selector; 43 | 44 | public void init() throws Exception { 45 | // Initialize the networking objects. 46 | selector = Selector.open(); 47 | serverChannel = ServerSocketChannel.open(); 48 | 49 | // ... and configure them! 50 | serverChannel.configureBlocking(false); 51 | serverChannel.socket().bind(Server.getInstance().getAddress()); 52 | serverChannel.register(selector, SelectionKey.OP_ACCEPT); 53 | 54 | // Finally, initialize whatever else we need. 55 | clientMap = new HashMap<>(); 56 | } 57 | 58 | public void tick() { 59 | // Remove disconnected clients 60 | clientMap.entrySet().removeIf(e -> !e.getKey().isValid()); 61 | 62 | // Remove clients which timed out during login 63 | List timedOutKeys = clientMap.entrySet().stream() 64 | .filter(e -> e.getValue().getTimeoutStopwatch().elapsed() > 5000) 65 | .map(Map.Entry::getKey) 66 | .collect(Collectors.toList()); 67 | timedOutKeys.forEach(e -> { 68 | clientMap.get(e).disconnect(); 69 | clientMap.remove(e); 70 | e.cancel(); 71 | }); 72 | 73 | // Handle all network events 74 | try { 75 | selector.selectNow(); 76 | 77 | for (SelectionKey selectionKey : selector.selectedKeys()) { 78 | if (selectionKey.isAcceptable()) { // Accept new connections. 79 | accept(10); 80 | } 81 | 82 | if (selectionKey.isReadable()) { // Client handles the packet. 83 | clientMap.get(selectionKey).handleIncomingData(); 84 | } 85 | } 86 | } catch (Exception ex) { 87 | ex.printStackTrace(); 88 | } 89 | 90 | // Check if connection throttling needs a clear 91 | if (System.currentTimeMillis() % ConnectionThrottle.COOLDOWN == 0) { 92 | ConnectionThrottle.clear(); 93 | } 94 | } 95 | 96 | public void cleanup() { 97 | Map client = new HashMap<>(clientMap); 98 | client.forEach((k, v) -> { 99 | if (v.getConnectionStage() != Client.ConnectionStage.LOGGED_OUT) { 100 | v.disconnect(); 101 | } 102 | }); 103 | } 104 | 105 | /** 106 | * Accepts up to n incoming connections. 107 | */ 108 | private void accept(int n) throws IOException { 109 | SocketChannel socket; 110 | 111 | /* 112 | * Here we use a for loop so that we can accept multiple clients per 113 | * tick for lower latency. We limit the amount of clients that we 114 | * accept per tick to better combat potential denial of service type 115 | * attacks. 116 | */ 117 | for (int i = 0; i < n; i++) { 118 | socket = serverChannel.accept(); 119 | 120 | if (socket == null) { 121 | // No more connections to accept (as this one was invalid). 122 | break; 123 | } 124 | 125 | // Register the connection 126 | HostGateway.enter(socket.socket().getInetAddress().getHostAddress()); 127 | 128 | // Set up the new connection. 129 | socket.configureBlocking(false); 130 | SelectionKey key = socket.register(selector, SelectionKey.OP_READ); 131 | Client client = new Player(key); 132 | System.out.println("Accepted " + client + "."); 133 | clientMap.put(key, client); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/player/skills/Skills.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.player.skills; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import static com.rs.entity.player.skills.SkillType.*; 23 | 24 | public class Skills { 25 | 26 | public static final int MAX_EXPERIENCE = 200_000_000; 27 | public static final int MIN_EXPERIENCE = 0; 28 | /** 29 | * A mapping of level i to the experience required to achieve it. 30 | */ 31 | public static final int[] EXPERIENCE_TABLE = { 32 | 0, 0, 83, 174, 276, 388, 512, 650, 801, 969, 1154, 1358, 1584, 1833, 2107, 2411, 2746, 3115, 3523, 3973, 33 | 4470, 5018, 5624, 6291, 7028, 7842, 8740, 9730, 10824, 12031, 13363, 14833, 16456, 18247, 20224, 22406, 34 | 24815, 27473, 30408, 33648, 37224, 41171, 45529, 50339, 55649, 61512, 67983, 75127, 83014, 91721, 101333, 35 | 111945, 123660, 136594, 150872, 166636, 184040, 203254, 224466, 247886, 273742, 302288, 333804, 368599, 36 | 407015, 449428, 496254, 547953, 605032, 668051, 737627, 814445, 899257, 992895, 1096278, 1210421, 1336443, 37 | 1475581, 1629200, 1798808, 1986068, 2192818, 2421087, 2673114, 2951373, 3258594, 3597792, 3972294, 4385776, 38 | 4842295, 5346332, 5902831, 6517253, 7195629, 7944614, 8771558, 9684577, 10692629, 11805606, 13034431 39 | }; 40 | private final List skills; 41 | private transient int combatLevel; 42 | private transient int totalLevel; 43 | 44 | public Skills(List skills) { 45 | this.skills = skills; 46 | updateTotalLevel(); 47 | updateCombatLevel(); 48 | } 49 | 50 | public Skills() { 51 | skills = new ArrayList<>(); 52 | reset(); 53 | } 54 | 55 | public void reset() { 56 | skills.clear(); 57 | 58 | for (SkillType type : SkillType.values()) { 59 | Skill skill = new Skill(type); 60 | skills.add(skill); 61 | } 62 | addExperience(HITPOINTS, 1154); 63 | updateTotalLevel(); 64 | updateCombatLevel(); 65 | } 66 | 67 | public int experience(SkillType type) { 68 | return skill(type).getExperience(); 69 | } 70 | 71 | public int level(SkillType type) { 72 | return skill(type).getLevel(); 73 | } 74 | 75 | public int maxLevel(SkillType type) { 76 | return skill(type).getMaxLevel(); 77 | } 78 | 79 | public void addExperience(SkillType type, int experience) { 80 | Skill skill = skill(type); 81 | setExperience(type, normalizeExp(skill.getExperience() + experience)); 82 | } 83 | 84 | public void setExperience(SkillType type, int newExperience) { 85 | newExperience = normalizeExp(newExperience); 86 | 87 | Skill skill = skill(type); 88 | int newLevel = levelForExp(newExperience); 89 | int levelChange = newLevel - skill.getMaxLevel(); 90 | skill.setExperience(newExperience); 91 | 92 | if (levelChange != 0) { 93 | updateTotalLevel(levelChange); 94 | skill.setMaxLevel(skill.getMaxLevel() + levelChange); 95 | skill.setLevel(skill.getLevel() + levelChange); 96 | 97 | if (type.isCombatSkill()) { 98 | updateCombatLevel(); 99 | } 100 | } 101 | } 102 | 103 | private void updateTotalLevel(int change) { 104 | totalLevel += change; 105 | } 106 | 107 | public void updateTotalLevel() { 108 | totalLevel = skills.stream().mapToInt(Skill::getMaxLevel).sum(); 109 | } 110 | 111 | public void updateCombatLevel() { 112 | combatLevel = combatLevel(maxLevel(ATTACK), maxLevel(STRENGTH), maxLevel(MAGIC), maxLevel(RANGED), 113 | maxLevel(DEFENCE), maxLevel(HITPOINTS), maxLevel(PRAYER)); 114 | } 115 | 116 | private Skill skill(SkillType type) { 117 | return skills.get(type.ordinal()); 118 | } 119 | 120 | public int getCombatLevel() { 121 | return combatLevel; 122 | } 123 | 124 | public int getTotalLevel() { 125 | return totalLevel; 126 | } 127 | 128 | public static int combatLevel(double atk, double str, double mag, double rng, double def, double hp, double pry) { 129 | // Source: http://runescape.wikia.com/wiki/Combat_level#History 130 | double k = 1.3d * Math.max(atk + str, Math.max(2 * mag, 2 * rng)); 131 | double total = k + def + hp + Math.floor(0.5d * pry); 132 | return (int) Math.floor(total / 4); 133 | } 134 | 135 | public static int levelForExp(int exp) { 136 | // Source: https://www.reddit.com/r/2007scape/comments/3idn2b/exp_to_lvl_formula/cufhr5q 137 | for (int level = 0; level < EXPERIENCE_TABLE.length - 1; level++) { 138 | if (EXPERIENCE_TABLE[level + 1] > exp) 139 | return level; 140 | } 141 | return 99; 142 | } 143 | 144 | public static int normalizeExp(int exp) { 145 | return Math.min(MAX_EXPERIENCE, Math.max(exp, MIN_EXPERIENCE)); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/player/PlayerUpdateContext.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity.player; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.EntityUpdateContext; 20 | import com.rs.net.StreamBuffer; 21 | 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * The update flags and buffer caches for a {@link Player}. 26 | */ 27 | public final class PlayerUpdateContext extends EntityUpdateContext { 28 | 29 | private final StreamBuffer.BufferCache regularBufferCache = new StreamBuffer.BufferCache(); 30 | private final StreamBuffer.BufferCache forcedAppearanceBufferCache = new StreamBuffer.BufferCache(); 31 | private final StreamBuffer.BufferCache noChatBufferCache = new StreamBuffer.BufferCache(); 32 | private final StreamBuffer.BufferCache forcedAppearanceNoChatBufferCache = new StreamBuffer.BufferCache(); 33 | private boolean asyncMovementUpdateRequired = false; 34 | private boolean publicChatUpdateRequired = false; 35 | private boolean appearanceUpdateRequired = false; 36 | 37 | public int mask() { 38 | return mask(false, false); 39 | } 40 | 41 | public int mask(boolean forceAppearance, boolean noPublicChat) { 42 | int mask = 0x0; 43 | 44 | if (isAsyncMovementUpdateRequired()) { 45 | mask |= 0x400; 46 | } 47 | 48 | if (isGraphicsUpdateRequired()) { 49 | mask |= 0x100; 50 | } 51 | 52 | if (isAnimationUpdateRequired()) { 53 | mask |= 0x8; 54 | } 55 | 56 | if (isForcedChatUpdateRequired()) { 57 | mask |= 0x4; 58 | } 59 | 60 | if (isPublicChatUpdateRequired() && !noPublicChat) { 61 | mask |= 0x80; 62 | } 63 | 64 | if (isInteractingNpcUpdateRequired()) { 65 | mask |= 0x1; 66 | } 67 | 68 | if (isAppearanceUpdateRequired() || forceAppearance) { 69 | mask |= 0x10; 70 | } 71 | 72 | if (isFaceCoordinatesUpdateRequired()) { 73 | mask |= 0x2; 74 | } 75 | 76 | if (isPrimaryHitUpdateRequired()) { 77 | mask |= 0x20; 78 | } 79 | 80 | if (isSecondaryHitUpdateRequired()) { 81 | mask |= 0x200; 82 | } 83 | return mask; 84 | } 85 | 86 | public void setUpdateRequired() { 87 | super.setUpdateRequired(); 88 | setAllBuffersOutdated(); 89 | } 90 | 91 | public void setAsyncMovementUpdateRequired() { 92 | setUpdateRequired(); 93 | asyncMovementUpdateRequired = true; 94 | } 95 | 96 | public void setPublicChatUpdateRequired() { 97 | setUpdateRequired(); 98 | publicChatUpdateRequired = true; 99 | } 100 | 101 | public void setAppearanceUpdateRequired() { 102 | setUpdateRequired(); 103 | appearanceUpdateRequired = true; 104 | } 105 | 106 | public boolean isAsyncMovementUpdateRequired() { 107 | return asyncMovementUpdateRequired; 108 | } 109 | 110 | public boolean isPublicChatUpdateRequired() { 111 | return publicChatUpdateRequired; 112 | } 113 | 114 | public boolean isAppearanceUpdateRequired() { 115 | return appearanceUpdateRequired; 116 | } 117 | 118 | /** 119 | * Resets all update flags. 120 | */ 121 | public void resetFlags() { 122 | super.resetFlags(); 123 | asyncMovementUpdateRequired = false; 124 | publicChatUpdateRequired = false; 125 | appearanceUpdateRequired = false; 126 | setAllBuffersOutdated(); 127 | } 128 | 129 | public void setBuffer(boolean forceAppearance, boolean noPublicChat, ByteBuffer buffer) { 130 | if (!forceAppearance && !noPublicChat) { 131 | regularBufferCache.setBuffer(buffer); 132 | } else if (forceAppearance && !noPublicChat) { 133 | forcedAppearanceBufferCache.setBuffer(buffer); 134 | } else if (!forceAppearance && noPublicChat) { 135 | noChatBufferCache.setBuffer(buffer); 136 | } else { 137 | forcedAppearanceNoChatBufferCache.setBuffer(buffer); 138 | } 139 | } 140 | 141 | public ByteBuffer getBuffer(boolean forceAppearance, boolean noPublicChat) { 142 | if (!forceAppearance && !noPublicChat && !regularBufferCache.isOutdated()) { 143 | return regularBufferCache.getBuffer(); 144 | } else if (forceAppearance && !noPublicChat && !forcedAppearanceBufferCache.isOutdated()) { 145 | return forcedAppearanceBufferCache.getBuffer(); 146 | } else if (!forceAppearance && noPublicChat && !noChatBufferCache.isOutdated()) { 147 | return noChatBufferCache.getBuffer(); 148 | } else if (forceAppearance && noPublicChat && !forcedAppearanceNoChatBufferCache.isOutdated()) { 149 | return forcedAppearanceNoChatBufferCache.getBuffer(); 150 | } 151 | return null; 152 | } 153 | 154 | private void setAllBuffersOutdated() { 155 | regularBufferCache.setOutdated(); 156 | forcedAppearanceBufferCache.setOutdated(); 157 | noChatBufferCache.setOutdated(); 158 | forcedAppearanceNoChatBufferCache.setOutdated(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /plugins/GroovyBootstrap.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | 18 | 19 | import com.rs.plugin.EventListener 20 | import com.rs.plugin.PluginHandler 21 | import com.rs.plugin.Bootstrap 22 | import com.rs.plugin.event.* 23 | import com.rs.plugin.listener.* 24 | import groovy.io.FileType 25 | 26 | /** 27 | * The plugin bootstrap, this is the object which receives events from the Java portion of the server. 28 | * It then forwards these events to the groovy plugins which it has loaded, depending on which interfaces 29 | * each extends. 30 | * Plugins extending Bootstrap are not loaded by this class, they are only loaded in the PluginHandler. 31 | * This must extend and implement the methods of each interface in com.rs.plugin.listener which it desires to handle. 32 | */ 33 | class GroovyBootstrap implements Bootstrap { 34 | 35 | private static final GroovyClassLoader CLASS_LOADER = new GroovyClassLoader() 36 | Map> plugins = new HashMap<>() 37 | 38 | void resetPlugins() { 39 | getPlugins(PluginStateListener.class).forEach { l -> l.unloaded() } 40 | plugins.clear() 41 | } 42 | 43 | void addPlugin(Class interfaceClass, Object instance) { 44 | getPlugins(interfaceClass).add(instance) 45 | } 46 | 47 | List getPlugins(Class interfaceClass) { 48 | if (!plugins.containsKey(interfaceClass)) { 49 | plugins.put interfaceClass, new ArrayList<>() 50 | } 51 | return plugins.get(interfaceClass) 52 | } 53 | 54 | boolean hasInterface(Class clazz, Class target) { 55 | if (target in clazz.getInterfaces()) { 56 | return true 57 | } else { 58 | for (Class i in clazz.getInterfaces()) { 59 | if (hasInterface(i, target)) { 60 | return true 61 | } 62 | } 63 | return false 64 | } 65 | } 66 | 67 | /** 68 | * Loads all supported plugins, as defined by SUPPORTED_INTERFACES. 69 | * This also performs the functionality of a reload. 70 | */ 71 | void load() { 72 | // Clear plugins 73 | resetPlugins() 74 | 75 | // Load new plugins 76 | File pluginsDir = new File(PluginHandler.PLUGINS_DIRECTORY) 77 | pluginsDir.eachFileRecurse(FileType.FILES) { file -> 78 | if (!file.name.endsWith('.groovy')) 79 | return 80 | 81 | Class cls = CLASS_LOADER.parseClass(file) 82 | 83 | // Skip if its a bootstrap 84 | if (Bootstrap.class in cls.getInterfaces()) { 85 | return 86 | } 87 | 88 | // Load 89 | Object obj = cls.newInstance() 90 | cls.getInterfaces().each { i -> 91 | if (hasInterface(i, EventListener.class)) { 92 | addPlugin(i, obj) 93 | } 94 | } 95 | } 96 | 97 | // Dispatch loaded events 98 | getPlugins(PluginStateListener.class).forEach { l -> l.loaded() } 99 | } 100 | 101 | void actionButton(ActionButtonEvent e) { 102 | getPlugins(ActionButtonListener.class).forEach { l -> l.actionButton e } 103 | } 104 | 105 | void command(CommandEvent e) { 106 | getPlugins(CommandListener.class).forEach { l -> l.command e } 107 | } 108 | 109 | void modifyChatMode(ModifyChatModeEvent e) { 110 | getPlugins(MessageConfigListener.class).forEach { l -> l.modifyChatMode e } 111 | } 112 | 113 | void addFriend(ModifyPlayerListEvent e) { 114 | getPlugins(MessageConfigListener.class).forEach { l -> l.addFriend e } 115 | } 116 | 117 | void removeFriend(ModifyPlayerListEvent e) { 118 | getPlugins(MessageConfigListener.class).forEach { l -> l.removeFriend e } 119 | } 120 | 121 | void addIgnore(ModifyPlayerListEvent e) { 122 | getPlugins(MessageConfigListener.class).forEach { l -> l.addIgnore e } 123 | } 124 | 125 | void removeIgnore(ModifyPlayerListEvent e) { 126 | getPlugins(MessageConfigListener.class).forEach { l -> l.removeIgnore e } 127 | } 128 | 129 | void publicMessage(PublicMessageEvent e) { 130 | getPlugins(MessageListener.class).forEach { l -> l.publicMessage e } 131 | } 132 | 133 | void privateMessage(PrivateMessageEvent e) { 134 | getPlugins(MessageListener.class).forEach { l -> l.privateMessage e } 135 | } 136 | 137 | void logIn(PlayerLoggedInEvent e) { 138 | getPlugins(PlayerConnectivityListener.class).forEach { l -> l.logIn e } 139 | } 140 | 141 | void logOut(PlayerLoggedOutEvent e) { 142 | getPlugins(PlayerConnectivityListener.class).forEach { l -> l.logOut e } 143 | } 144 | 145 | void reportAbuse(ReportAbuseEvent e) { 146 | getPlugins(ReportAbuseListener.class).forEach { l -> l.reportAbuse e } 147 | } 148 | 149 | void loaded() { 150 | getPlugins(PluginStateListener.class).forEach { l -> l.loaded() } 151 | } 152 | 153 | void unloaded() { 154 | getPlugins(PluginStateListener.class).forEach { l -> l.unloaded() } 155 | } 156 | 157 | void tick() { 158 | getPlugins(TickListener.class).forEach { l -> l.tick() } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /plugins/packets/CommandHandler.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | import com.rs.WorldHandler 18 | import com.rs.entity.Position 19 | import com.rs.entity.npc.Npc 20 | import com.rs.entity.player.Player 21 | import com.rs.entity.player.PlayerAttributes 22 | import com.rs.entity.action.Animation 23 | import com.rs.entity.action.Graphics 24 | import com.rs.plugin.event.CommandEvent 25 | import com.rs.plugin.listener.CommandListener 26 | import com.rs.entity.player.skills.Skills 27 | import com.rs.entity.player.skills.SkillType 28 | 29 | class CommandHandler implements CommandListener { 30 | 31 | void command(CommandEvent evt) { 32 | Player player = evt.getPlayer() 33 | PlayerAttributes attributes = player.getAttributes() 34 | String commandName = evt.getCommandName() 35 | String[] args = evt.getArgs() 36 | 37 | if (commandName == "hide") { 38 | player.setHidden true 39 | } 40 | 41 | if (commandName == "unhide") { 42 | player.setHidden false 43 | } 44 | 45 | if (commandName == "fchat") { 46 | if (args.length < 1) { 47 | player.sendMessage("Syntax: ::fchat [text]") 48 | return 49 | } 50 | player.setForceChatText args.join(" ") 51 | player.getUpdateContext().setForcedChatUpdateRequired() 52 | } 53 | 54 | if (commandName == "gfx") { 55 | if (args.length < 2) { 56 | player.sendMessage("Syntax: ::gfx [id] [delay]") 57 | return 58 | } 59 | player.startGraphic new Graphics(args[0].toInteger(), args[1].toInteger()) 60 | } 61 | 62 | if (commandName == "anim") { 63 | if (args.length < 2) { 64 | player.sendMessage("Syntax: ::anim [id] [delay]") 65 | return 66 | } 67 | player.startAnimation new Animation(args[0].toInteger(), args[1].toInteger()) 68 | } 69 | 70 | if (commandName == "interface") { 71 | if (args.length < 1) { 72 | player.sendMessage("Syntax: ::interface [id]") 73 | return 74 | } 75 | player.sendInterface args[0].toInteger() 76 | player.setCurrentInterfaceId args[0].toInteger() 77 | } 78 | 79 | if (commandName == "closeinterface") { 80 | player.sendClearScreen() 81 | player.setCurrentInterfaceId(-1) 82 | } 83 | 84 | if (commandName == "energy") { 85 | if (args.length < 1) { 86 | player.sendMessage("Syntax: ::energy [amount]") 87 | return 88 | } 89 | player.getAttributes().setRunEnergy args[0].toInteger() 90 | player.sendRunEnergy() 91 | } 92 | 93 | if (commandName == "master") { 94 | Skills skills = attributes.getSkills() 95 | 96 | for (SkillType type : SkillType.values()) { 97 | skills.setExperience(type, 13034431) 98 | } 99 | player.sendSkills() 100 | player.getUpdateContext().setAppearanceUpdateRequired() 101 | } 102 | 103 | if (commandName == "noob") { 104 | Skills skills = attributes.getSkills() 105 | skills.reset() 106 | player.sendSkills() 107 | player.getUpdateContext().setAppearanceUpdateRequired() 108 | } 109 | 110 | if (commandName == "empty") { 111 | attributes.emptyInventory player 112 | } 113 | 114 | if (commandName == "pickup" || commandName == "item") { 115 | if (args.length < 1) { 116 | player.sendMessage("Syntax: ::" + commandName + " [id] [amount]") 117 | return 118 | } 119 | int id = args[0].toInteger() 120 | int amount = args.length > 1 ? args[1].toInteger() : 1 121 | attributes.addInventoryItem id, amount, player 122 | player.sendInventory() 123 | } 124 | 125 | if (commandName == "tele") { 126 | if (args.length < 2) { 127 | player.sendMessage("Syntax: ::tele [x] [y] [z]") 128 | return 129 | } 130 | int x = args[0].toInteger() 131 | int y = args[1].toInteger() 132 | int z = args.length > 2 ? args[2].toInteger() : player.getPosition().getZ() 133 | player.teleport new Position(x, y, z) 134 | } 135 | 136 | if (commandName == "npc") { 137 | if (args.length < 1) { 138 | player.sendMessage("Syntax: ::npc [id] [x] [y] [z]") 139 | return 140 | } 141 | Npc npc = new Npc(args[0].toInteger()) 142 | npc.getPosition().setAs(player.getPosition()) 143 | 144 | if (args.length >= 3) { 145 | npc.getPosition().setX(args[1].toInteger()) 146 | npc.getPosition().setY(args[2].toInteger()) 147 | } 148 | 149 | if (args.length >= 4) { 150 | npc.getPosition().setZ(args[3].toInteger()) 151 | } 152 | WorldHandler.getInstance().register(npc) 153 | } 154 | 155 | if (commandName == "mypos") { 156 | player.sendMessage "You are at: ${player.getPosition()}" 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/com/rs/plugin/PluginHandler.java: -------------------------------------------------------------------------------- 1 | package com.rs.plugin; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.action.PublicChat; 20 | import com.rs.entity.player.Player; 21 | import com.rs.entity.player.infractions.ReportAbuse; 22 | import com.rs.plugin.event.*; 23 | import groovy.lang.GroovyClassLoader; 24 | 25 | import java.io.File; 26 | import java.nio.file.Files; 27 | import java.nio.file.Path; 28 | import java.nio.file.Paths; 29 | import java.util.*; 30 | import java.util.function.Consumer; 31 | import java.util.stream.Collectors; 32 | import java.util.stream.Stream; 33 | 34 | /** 35 | * A simple class that provides methods to register, unregister, and run 36 | * execution for all registered plugins. 37 | * 38 | * @author Pure_ 39 | */ 40 | public final class PluginHandler { 41 | 42 | // TODO dispatch loaded() + unloaded() 43 | 44 | /** 45 | * The base directory of all plugins. 46 | */ 47 | public static final String PLUGINS_DIRECTORY = "./plugins/"; 48 | private static final GroovyClassLoader classLoader = new GroovyClassLoader(); 49 | /** 50 | * All registered bootstraps. 51 | */ 52 | private static final List bootstraps = new ArrayList<>(); 53 | 54 | /** 55 | * Loads all bootstraps. 56 | */ 57 | public static void load() throws Exception { 58 | boolean foundBootstrap = false; 59 | 60 | try (Stream paths = Files.walk(Paths.get(PLUGINS_DIRECTORY))) { 61 | for (File f : paths.filter(p -> !Files.isDirectory(p) && p.getFileName().toString().endsWith(".groovy")) 62 | .map(Path::toFile) 63 | .collect(Collectors.toList())) { 64 | Class cls = classLoader.parseClass(f); 65 | 66 | if (Arrays.asList(cls.getInterfaces()).contains(Bootstrap.class)) { 67 | Bootstrap b = (Bootstrap) cls.getDeclaredConstructor().newInstance(); 68 | b.load(); 69 | bootstraps.add(b); 70 | foundBootstrap = true; 71 | } 72 | } 73 | } 74 | 75 | if (!foundBootstrap) { 76 | System.err.println("Unable to find a suitable bootstrap!"); 77 | } 78 | } 79 | 80 | /** 81 | * Reloads all plugins, by calling {@link Bootstrap#load()}. 82 | */ 83 | public static void reload() { 84 | bootstraps.forEach(Bootstrap::load); 85 | } 86 | 87 | /** 88 | * Performs an action on each registered bootstrap. 89 | * @param action 90 | */ 91 | public static void forEach(Consumer action) { 92 | bootstraps.forEach(action); 93 | } 94 | 95 | public static void dispatchLogin(Player player, boolean newPlayer) { 96 | PlayerLoggedInEvent e = new PlayerLoggedInEvent(player, newPlayer); 97 | forEach(b -> b.logIn(e)); 98 | } 99 | 100 | public static void dispatchLogout(Player player) { 101 | PlayerLoggedOutEvent e = new PlayerLoggedOutEvent(player); 102 | forEach(b -> b.logOut(e)); 103 | } 104 | 105 | public static void dispatchModifyChatMode(Player player, int publicChatMode, int privateChatMode, int tradeMode) { 106 | ModifyChatModeEvent e = new ModifyChatModeEvent(player, publicChatMode, privateChatMode, tradeMode); 107 | forEach(b -> b.modifyChatMode(e)); 108 | } 109 | 110 | public static void dispatchActionButton(Player player, int actionButtonId) { 111 | ActionButtonEvent e = new ActionButtonEvent(player, actionButtonId); 112 | forEach(b -> b.actionButton(e)); 113 | } 114 | 115 | public static void dispatchAddIgnore(Player player, long target) { 116 | ModifyPlayerListEvent e = new ModifyPlayerListEvent(player, target); 117 | forEach(b -> b.addIgnore(e)); 118 | } 119 | 120 | public static void dispatchRemoveIgnore(Player player, long target) { 121 | ModifyPlayerListEvent e = new ModifyPlayerListEvent(player, target); 122 | forEach(b -> b.removeIgnore(e)); 123 | } 124 | 125 | public static void dispatchAddFriend(Player player, long target) { 126 | ModifyPlayerListEvent e = new ModifyPlayerListEvent(player, target); 127 | forEach(b -> b.addFriend(e)); 128 | } 129 | 130 | public static void dispatchRemoveFriend(Player player, long target) { 131 | ModifyPlayerListEvent e = new ModifyPlayerListEvent(player, target); 132 | forEach(b -> b.removeFriend(e)); 133 | } 134 | 135 | public static void dispatchPrivateMessage(Player player, long target, byte[] text) { 136 | PrivateMessageEvent e = new PrivateMessageEvent(player, target, text); 137 | forEach(b -> b.privateMessage(e)); 138 | } 139 | 140 | public static void dispatchCommand(Player player, String commandName, String[] args) { 141 | CommandEvent e = new CommandEvent(player, commandName, args); 142 | forEach(b -> b.command(e)); 143 | } 144 | 145 | public static void dispatchPublicMessage(Player player, PublicChat publicChat) { 146 | PublicMessageEvent e = new PublicMessageEvent(player, publicChat); 147 | forEach(b -> b.publicMessage(e)); 148 | } 149 | 150 | public static void dispatchReportAbuse(Player player, ReportAbuse reportAbuse) { 151 | ReportAbuseEvent e = new ReportAbuseEvent(player, reportAbuse); 152 | forEach(b -> b.reportAbuse(e)); 153 | } 154 | 155 | public static void dispatchTick() throws Exception { 156 | for (Bootstrap b : bootstraps) { 157 | b.tick(); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /data/stackable.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/MovementHandler.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.player.Player; 20 | import com.rs.util.Tickable; 21 | 22 | import java.util.Deque; 23 | import java.util.LinkedList; 24 | 25 | /** 26 | * Handles the movement of an {@link Entity}. 27 | * 28 | * @author blakeman8192 29 | */ 30 | public final class MovementHandler implements Tickable { 31 | 32 | private final Entity entity; 33 | private final Deque waypoints = new LinkedList<>(); 34 | private boolean runPath = false; 35 | 36 | /** 37 | * Creates a new MovementHandler. 38 | */ 39 | public MovementHandler(Entity entity) { 40 | this.entity = entity; 41 | } 42 | 43 | public void tick() { 44 | Point walkPoint; 45 | Point runPoint = null; 46 | 47 | // Handle the movement. 48 | walkPoint = waypoints.poll(); 49 | 50 | // Handling run toggling 51 | if (entity instanceof Player) { 52 | Player player = (Player)entity; 53 | 54 | if (player.getAttributes().getSettings().isRunToggled() || isRunPath()) { 55 | if (player.getAttributes().hasRunEnergy()) { 56 | runPoint = waypoints.poll(); 57 | } else { // Player is out of energy 58 | player.sendClientSetting(173, 0); 59 | player.getAttributes().getSettings().setRunToggled(false); 60 | setRunPath(false); 61 | runPoint = null; 62 | } 63 | } 64 | } else if (isRunPath()) { 65 | runPoint = waypoints.poll(); 66 | } 67 | 68 | // Walking 69 | if (walkPoint != null && walkPoint.getDirection() != -1) { 70 | entity.getPosition().move(Position.DIRECTION_DELTA_X[walkPoint.getDirection()], Position.DIRECTION_DELTA_Y[walkPoint.getDirection()]); 71 | entity.setPrimaryDirection(walkPoint.getDirection()); 72 | } 73 | 74 | // Running 75 | if (runPoint != null && runPoint.getDirection() != -1) { 76 | entity.getPosition().move(Position.DIRECTION_DELTA_X[runPoint.getDirection()], Position.DIRECTION_DELTA_Y[runPoint.getDirection()]); 77 | entity.setSecondaryDirection(runPoint.getDirection()); 78 | 79 | // Reducing energy 80 | if (entity instanceof Player) { 81 | Player player = (Player)entity; 82 | player.getAttributes().decreaseRunEnergy(player.getRunEnergyDecrement()); 83 | player.sendRunEnergy(); 84 | } 85 | } else { 86 | // Restoring run energy 87 | if (entity instanceof Player) { 88 | Player player = (Player)entity; 89 | 90 | if (player.getAttributes().getRunEnergy() != 100) { 91 | player.getAttributes().increaseRunEnergy(player.getRunEnergyIncrement()); 92 | player.sendRunEnergy(); 93 | } 94 | } 95 | } 96 | 97 | // Check for region changes. 98 | int deltaX = entity.getPosition().getX() - entity.getCurrentRegion().getRegionX() * 8; 99 | int deltaY = entity.getPosition().getY() - entity.getCurrentRegion().getRegionY() * 8; 100 | 101 | if (deltaX < 16 || deltaX >= 88 || deltaY < 16 || deltaY > 88) { 102 | if (entity instanceof Player) { 103 | ((Player)entity).sendMapRegion(); 104 | } 105 | } 106 | } 107 | 108 | /** 109 | * Resets the walking queue. 110 | */ 111 | public void reset() { 112 | setRunPath(false); 113 | waypoints.clear(); 114 | 115 | // Set the base point as this position. 116 | Position p = entity.getPosition(); 117 | waypoints.add(new Point(p.getX(), p.getY(), -1)); 118 | } 119 | 120 | /** 121 | * Finishes the current path. 122 | */ 123 | public void finish() { 124 | waypoints.removeFirst(); 125 | } 126 | 127 | /** 128 | * Adds a position to the path. 129 | * 130 | * @param position the position 131 | */ 132 | public void addToPath(Position position) { 133 | if (waypoints.size() == 0) { 134 | reset(); 135 | } 136 | Point last = waypoints.peekLast(); 137 | int deltaX = position.getX() - last.getX(); 138 | int deltaY = position.getY() - last.getY(); 139 | 140 | int max = Math.max(Math.abs(deltaX), Math.abs(deltaY)); 141 | 142 | for (int i = 0; i < max; i++) { 143 | if (deltaX < 0) { 144 | deltaX++; 145 | } else if (deltaX > 0) { 146 | deltaX--; 147 | } 148 | 149 | if (deltaY < 0) { 150 | deltaY++; 151 | } else if (deltaY > 0) { 152 | deltaY--; 153 | } 154 | addStep(position.getX() - deltaX, position.getY() - deltaY); 155 | } 156 | } 157 | 158 | /** 159 | * Adds a step. 160 | * 161 | * @param x the X coordinate 162 | * @param y the Y coordinate 163 | */ 164 | private void addStep(int x, int y) { 165 | if (waypoints.size() >= 100) { 166 | return; 167 | } 168 | Point last = waypoints.peekLast(); 169 | int deltaX = x - last.getX(); 170 | int deltaY = y - last.getY(); 171 | int direction = Position.direction(deltaX, deltaY); 172 | 173 | if (direction > -1) { 174 | waypoints.add(new Point(x, y, direction)); 175 | } 176 | } 177 | 178 | /** 179 | * Gets whether or not we're running for the current path. 180 | */ 181 | public boolean isRunPath() { 182 | return runPath; 183 | } 184 | 185 | /** 186 | * Toggles running for the current path only. 187 | */ 188 | public void setRunPath(boolean runPath) { 189 | this.runPath = runPath; 190 | } 191 | 192 | /** 193 | * An internal Position type class with support for direction. 194 | * 195 | * @author blakeman8192 196 | */ 197 | private class Point extends Position { 198 | 199 | private int direction; 200 | 201 | /** 202 | * Creates a new Point. 203 | * 204 | * @param x the X coordinate 205 | * @param y the Y coordinate 206 | * @param direction the direction to this point 207 | */ 208 | public Point(int x, int y, int direction) { 209 | super(x, y); 210 | setDirection(direction); 211 | } 212 | 213 | /** 214 | * Gets the direction. 215 | * 216 | * @return the direction 217 | */ 218 | public int getDirection() { 219 | return direction; 220 | } 221 | 222 | /** 223 | * Sets the direction. 224 | * 225 | * @param direction the direction 226 | */ 227 | public void setDirection(int direction) { 228 | this.direction = direction; 229 | } 230 | 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /src/main/java/com/rs/entity/Position.java: -------------------------------------------------------------------------------- 1 | package com.rs.entity; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | /** 20 | * Represents the position of a player or NPC. 21 | * 22 | * @author blakeman8192 23 | */ 24 | public class Position { 25 | 26 | /** 27 | * Difference in X coordinates for directions array. 28 | */ 29 | static final byte[] DIRECTION_DELTA_X = new byte[] {-1, 0, 1, -1, 1, -1, 0, 1}; 30 | /** 31 | * Difference in Y coordinates for directions array. 32 | */ 33 | static final byte[] DIRECTION_DELTA_Y = new byte[] {1, 1, 1, 0, 0, -1, -1, -1}; 34 | private int x; 35 | private int y; 36 | private int z; 37 | 38 | /** 39 | * Creates a new Position with the specified coordinates. The Z coordinate 40 | * is set to 0. 41 | * 42 | * @param x the X coordinate 43 | * @param y the Y coordinate 44 | */ 45 | public Position(int x, int y) { 46 | this(x, y, 0); 47 | } 48 | 49 | /** 50 | * Creates a new Position with the specified coordinates. 51 | * 52 | * @param x the X coordinate 53 | * @param y the Y coordinate 54 | * @param z the Z coordinate 55 | */ 56 | public Position(int x, int y, int z) { 57 | this.setX(x); 58 | this.setY(y); 59 | this.setZ(z); 60 | } 61 | 62 | /** 63 | * Returns the delta coordinates. Note that the returned Position is not an 64 | * actual position, instead it's values represent the delta values between 65 | * the two arguments. 66 | * 67 | * @param a the first position 68 | * @param b the second position 69 | * @return the delta coordinates contained within a position 70 | */ 71 | public static Position delta(Position a, Position b) { 72 | return new Position(b.getX() - a.getX(), b.getY() - a.getY(), b.getZ() - a.getZ()); 73 | } 74 | 75 | /** 76 | * Calculates the direction between the two coordinates. 77 | * 78 | * @param dx the first coordinate 79 | * @param dy the second coordinate 80 | * @return the direction 81 | */ 82 | static int direction(int dx, int dy) { 83 | if (dx < 0) { 84 | if (dy < 0) { 85 | return 5; 86 | } else if (dy > 0) { 87 | return 0; 88 | } else { 89 | return 3; 90 | } 91 | } else if (dx > 0) { 92 | if (dy < 0) { 93 | return 7; 94 | } else if (dy > 0) { 95 | return 2; 96 | } else { 97 | return 4; 98 | } 99 | } else { 100 | if (dy < 0) { 101 | return 6; 102 | } else if (dy > 0) { 103 | return 1; 104 | } else { 105 | return -1; 106 | } 107 | } 108 | } 109 | 110 | @Override 111 | public String toString() { 112 | return "Position(" + x + ", " + y + ", " + z + ")"; 113 | } 114 | 115 | @Override 116 | public boolean equals(Object other) { 117 | if (other instanceof Position) { 118 | Position p = (Position) other; 119 | return x == p.x && y == p.y && z == p.z; 120 | } 121 | return false; 122 | } 123 | 124 | /** 125 | * Sets this position as the other position. Please use this method 126 | * instead of player.setPosition(other) because of reference conflicts 127 | * (if the other position gets modified, so will the players). 128 | * 129 | * @param other the other position 130 | */ 131 | public void setAs(Position other) { 132 | this.x = other.x; 133 | this.y = other.y; 134 | this.z = other.z; 135 | } 136 | 137 | /** 138 | * Moves the position. 139 | * 140 | * @param amountX the amount of X coordinates 141 | * @param amountY the amount of Y coordinates 142 | */ 143 | public void move(int amountX, int amountY) { 144 | setX(getX() + amountX); 145 | setY(getY() + amountY); 146 | } 147 | 148 | /** 149 | * Gets the X coordinate. 150 | * 151 | * @return the X coordinate 152 | */ 153 | public int getX() { 154 | return x; 155 | } 156 | 157 | /** 158 | * Sets the X coordinate. 159 | * 160 | * @param x the X coordinate 161 | */ 162 | public void setX(int x) { 163 | this.x = x; 164 | } 165 | 166 | /** 167 | * Gets the Y coordinate. 168 | * 169 | * @return the Y coordinate 170 | */ 171 | public int getY() { 172 | return y; 173 | } 174 | 175 | /** 176 | * Sets the Y coordinate. 177 | * 178 | * @param y the Y coordinate 179 | */ 180 | public void setY(int y) { 181 | this.y = y; 182 | } 183 | 184 | /** 185 | * Gets the Z coordinate. 186 | * 187 | * @return the Z coordinate. 188 | */ 189 | public int getZ() { 190 | return z; 191 | } 192 | 193 | /** 194 | * Sets the Z coordinate. 195 | * 196 | * @param z the Z coordinate 197 | */ 198 | public void setZ(int z) { 199 | this.z = z; 200 | } 201 | 202 | /** 203 | * Gets the X coordinate of the region containing this Position. 204 | * 205 | * @return the region X coordinate 206 | */ 207 | public int getRegionX() { 208 | return (x >> 3) - 6; 209 | } 210 | 211 | /** 212 | * Gets the Y coordinate of the region containing this Position. 213 | * 214 | * @return the region Y coordinate 215 | */ 216 | public int getRegionY() { 217 | return (y >> 3) - 6; 218 | } 219 | 220 | /** 221 | * Gets the local X coordinate relative to the base Position. 222 | * 223 | * @param base the base Position 224 | * @return the local X coordinate 225 | */ 226 | public int getLocalX(Position base) { 227 | return x - 8 * base.getRegionX(); 228 | } 229 | 230 | /** 231 | * Gets the local Y coordinate relative to the base Position. 232 | * 233 | * @param base the base Position. 234 | * @return the local Y coordinate. 235 | */ 236 | public int getLocalY(Position base) { 237 | return y - 8 * base.getRegionY(); 238 | } 239 | 240 | /** 241 | * Gets the local X coordinate relative to this Position. 242 | * 243 | * @return the local X coordinate 244 | */ 245 | public int getLocalX() { 246 | return getLocalX(this); 247 | } 248 | 249 | /** 250 | * Gets the local Y coordinate relative to this Position. 251 | * 252 | * @return the local Y coordinate.= 253 | */ 254 | public int getLocalY() { 255 | return getLocalY(this); 256 | } 257 | 258 | /** 259 | * Checks if this position is viewable from the other position. 260 | * 261 | * @param other the other position 262 | * @return true if it is viewable, false otherwise 263 | */ 264 | public boolean isViewableFrom(Position other) { 265 | Position p = delta(this, other); 266 | return p.z == 0 && p.x <= 14 && p.x >= -15 && p.y <= 14 && p.y >= -15; 267 | } 268 | 269 | } 270 | -------------------------------------------------------------------------------- /src/main/java/com/rs/Server.java: -------------------------------------------------------------------------------- 1 | package com.rs; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.google.inject.Guice; 20 | import com.google.inject.Inject; 21 | import com.google.inject.Injector; 22 | import com.google.inject.name.Named; 23 | import com.rs.io.PlayerFileHandler; 24 | import com.rs.plugin.PluginHandler; 25 | import com.rs.service.Service; 26 | import com.rs.util.AbstractCredentialValidator; 27 | import com.rs.util.EquipmentHelper; 28 | import com.rs.util.Misc; 29 | 30 | import java.io.File; 31 | import java.net.InetSocketAddress; 32 | import java.util.Set; 33 | 34 | /** 35 | * The main core of RuneSource. 36 | * 37 | * @author blakeman8192 38 | */ 39 | public final class Server implements Runnable, Service { 40 | 41 | 42 | /** 43 | * The amount of CPU processors available. 44 | */ 45 | public static final int CPU_CORES = Runtime.getRuntime().availableProcessors(); 46 | private static Server instance; 47 | private final String host; 48 | private final int port; 49 | private final int tickRate; 50 | private final Set services; 51 | private final PlayerFileHandler playerFileHandler; 52 | private final AbstractCredentialValidator credentialValidator; 53 | 54 | private Settings settings; 55 | private InetSocketAddress address; 56 | private Misc.Stopwatch cycleTimer; 57 | 58 | /** 59 | * Creates a new Server. 60 | * 61 | * @param host the host 62 | * @param port the port 63 | * @param tickRate the tick rate 64 | */ 65 | @Inject 66 | private Server(@Named("host") String host, @Named("port") int port, @Named("tickRate") int tickRate, 67 | Set services, PlayerFileHandler playerFileHandler, 68 | AbstractCredentialValidator credentialValidator) { 69 | this.host = host; 70 | this.port = port; 71 | this.tickRate = tickRate; 72 | this.services = services; 73 | this.playerFileHandler = playerFileHandler; 74 | this.credentialValidator = credentialValidator; 75 | } 76 | 77 | /** 78 | * The main method. 79 | */ 80 | public static void main(String[] args) { 81 | // Parse command line arguments 82 | String host = "127.0.0.1"; 83 | int port = 43594; 84 | int tickRate = 600; 85 | 86 | if (args.length == 3) { 87 | host = args[0]; 88 | port = Integer.parseInt(args[1]); 89 | tickRate = Integer.parseInt(args[2]); 90 | } else if (args.length != 0) { 91 | System.err.println("Usage: Server "); 92 | return; 93 | } 94 | 95 | // Validate command line arguments 96 | if (port < 0 || port > 65535) { 97 | System.err.println("Invalid port number, must be between 0 and 65535."); 98 | return; 99 | } 100 | 101 | if (tickRate < 0) { 102 | System.err.println("Invalid cycle rate, must be a positive integer."); 103 | return; 104 | } 105 | 106 | // Create server 107 | Injector injector = Guice.createInjector(new ServerModule(host, port, tickRate)); 108 | Server server = injector.getInstance(Server.class); 109 | 110 | // Start server 111 | setInstance(server); 112 | new Thread(getInstance()).start(); 113 | } 114 | 115 | @Override 116 | public void run() { 117 | try { 118 | address = new InetSocketAddress(host, port); 119 | System.out.println("Starting RuneSource on " + address + "..."); 120 | 121 | // Load settings 122 | settings = Settings.load("./data/settings.json"); 123 | 124 | // Set up out, err redirection 125 | new File("./data/logs").mkdir(); 126 | System.setOut(new Misc.TimestampLogger(System.out, "./data/logs/out.log")); 127 | System.setErr(new Misc.TimestampLogger(System.err, "./data/logs/err.log")); 128 | 129 | // Loading configuration 130 | Misc.Stopwatch timer = new Misc.Stopwatch(); 131 | EquipmentHelper.loadWeaponDefinitions("./data/weapons.json"); 132 | EquipmentHelper.sortEquipmentSlotDefinitions(); 133 | EquipmentHelper.loadStackableItems("./data/stackable.dat"); 134 | System.out.println("Loaded all configuration in " + timer.elapsed() + "ms"); 135 | 136 | // Loading plugins 137 | timer.reset(); 138 | PluginHandler.load(); 139 | System.out.println("Loaded all plugins in " + timer.elapsed() + "ms"); 140 | 141 | // Starting the server 142 | init(); 143 | System.out.println("Started as " + settings.getServerName()); 144 | 145 | while (true) { 146 | tick(); 147 | sleep(); 148 | } 149 | } catch (Exception ex) { 150 | ex.printStackTrace(); 151 | } 152 | } 153 | 154 | /** 155 | * Initialises the server. 156 | */ 157 | public void init() throws Exception { 158 | // Init 159 | for (Service service : services) { 160 | service.init(); 161 | } 162 | 163 | // Set shutdown hook 164 | Runtime.getRuntime().addShutdownHook(new Thread(this::cleanup)); 165 | 166 | cycleTimer = new Misc.Stopwatch(); 167 | } 168 | 169 | /** 170 | * Performs a server tick. 171 | */ 172 | public void tick() { 173 | services.forEach(Service::tick); 174 | } 175 | 176 | /** 177 | * Cleans up the server. 178 | */ 179 | public void cleanup() { 180 | services.forEach(Service::cleanup); 181 | } 182 | 183 | /** 184 | * Sleeps for the remainder of the tick time slice. 185 | */ 186 | private void sleep() throws InterruptedException { 187 | long sleepTime = tickRate - cycleTimer.elapsed(); 188 | 189 | if (sleepTime > 0) { 190 | Thread.sleep(sleepTime); 191 | } else { 192 | // The server has reached maximum load, players may now lag. 193 | System.out.println("[WARNING]: Server load: " + (100 + (Math.abs(sleepTime) / (tickRate / 100))) + "%!"); 194 | } 195 | cycleTimer.reset(); 196 | } 197 | 198 | public InetSocketAddress getAddress() { 199 | return address; 200 | } 201 | 202 | public PlayerFileHandler getPlayerFileHandler() { 203 | return playerFileHandler; 204 | } 205 | 206 | public AbstractCredentialValidator getCredentialValidator() { 207 | return credentialValidator; 208 | } 209 | 210 | public Settings getSettings() { 211 | return settings; 212 | } 213 | 214 | /** 215 | * Gets the server instance. 216 | */ 217 | public static Server getInstance() { 218 | return instance; 219 | } 220 | 221 | /** 222 | * Sets the server instance. 223 | */ 224 | public static void setInstance(Server instance) { 225 | if (Server.instance != null) { 226 | throw new IllegalStateException("Singleton already set!"); 227 | } 228 | Server.instance = instance; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /data/characters/mopar.json: -------------------------------------------------------------------------------- 1 | { 2 | "@type":"com.rs.entity.player.PlayerAttributes", 3 | "username":"mopar", 4 | "password":"test", 5 | "position":{ 6 | "x":3223, 7 | "y":3215, 8 | "z":0 9 | }, 10 | "privilege":{ 11 | "name":"REGULAR" 12 | }, 13 | "settings":{ 14 | "mouseButtons":{ 15 | "name":"TWO" 16 | }, 17 | "brightness":{ 18 | "name":"NORMAL" 19 | }, 20 | "chatEffects":true, 21 | "splitPrivateChat":true, 22 | "acceptAid":false, 23 | "runToggled":false, 24 | "autoRetaliate":false, 25 | "publicChatMode":0, 26 | "privateChatMode":0, 27 | "tradeMode":0 28 | }, 29 | "skills":{ 30 | "skills":{ 31 | "@type":"java.util.ArrayList", 32 | "@items":[ 33 | { 34 | "@type":"com.rs.entity.player.skills.Skill", 35 | "type":{ 36 | "combatSkill":true, 37 | "name":"ATTACK" 38 | }, 39 | "level":1, 40 | "maxLevel":1, 41 | "experience":0 42 | }, 43 | { 44 | "@type":"com.rs.entity.player.skills.Skill", 45 | "type":{ 46 | "combatSkill":true, 47 | "name":"DEFENCE" 48 | }, 49 | "level":1, 50 | "maxLevel":1, 51 | "experience":0 52 | }, 53 | { 54 | "@type":"com.rs.entity.player.skills.Skill", 55 | "type":{ 56 | "combatSkill":true, 57 | "name":"STRENGTH" 58 | }, 59 | "level":1, 60 | "maxLevel":1, 61 | "experience":0 62 | }, 63 | { 64 | "@type":"com.rs.entity.player.skills.Skill", 65 | "type":{ 66 | "combatSkill":true, 67 | "name":"HITPOINTS" 68 | }, 69 | "level":10, 70 | "maxLevel":10, 71 | "experience":1154 72 | }, 73 | { 74 | "@type":"com.rs.entity.player.skills.Skill", 75 | "type":{ 76 | "combatSkill":true, 77 | "name":"RANGED" 78 | }, 79 | "level":1, 80 | "maxLevel":1, 81 | "experience":0 82 | }, 83 | { 84 | "@type":"com.rs.entity.player.skills.Skill", 85 | "type":{ 86 | "combatSkill":true, 87 | "name":"PRAYER" 88 | }, 89 | "level":1, 90 | "maxLevel":1, 91 | "experience":0 92 | }, 93 | { 94 | "@type":"com.rs.entity.player.skills.Skill", 95 | "type":{ 96 | "combatSkill":true, 97 | "name":"MAGIC" 98 | }, 99 | "level":1, 100 | "maxLevel":1, 101 | "experience":0 102 | }, 103 | { 104 | "@type":"com.rs.entity.player.skills.Skill", 105 | "type":{ 106 | "combatSkill":false, 107 | "name":"COOKING" 108 | }, 109 | "level":1, 110 | "maxLevel":1, 111 | "experience":0 112 | }, 113 | { 114 | "@type":"com.rs.entity.player.skills.Skill", 115 | "type":{ 116 | "combatSkill":false, 117 | "name":"WOODCUTTING" 118 | }, 119 | "level":1, 120 | "maxLevel":1, 121 | "experience":0 122 | }, 123 | { 124 | "@type":"com.rs.entity.player.skills.Skill", 125 | "type":{ 126 | "combatSkill":false, 127 | "name":"FLETCHING" 128 | }, 129 | "level":1, 130 | "maxLevel":1, 131 | "experience":0 132 | }, 133 | { 134 | "@type":"com.rs.entity.player.skills.Skill", 135 | "type":{ 136 | "combatSkill":false, 137 | "name":"FISHING" 138 | }, 139 | "level":1, 140 | "maxLevel":1, 141 | "experience":0 142 | }, 143 | { 144 | "@type":"com.rs.entity.player.skills.Skill", 145 | "type":{ 146 | "combatSkill":false, 147 | "name":"FIREMAKING" 148 | }, 149 | "level":1, 150 | "maxLevel":1, 151 | "experience":0 152 | }, 153 | { 154 | "@type":"com.rs.entity.player.skills.Skill", 155 | "type":{ 156 | "combatSkill":false, 157 | "name":"CRAFTING" 158 | }, 159 | "level":1, 160 | "maxLevel":1, 161 | "experience":0 162 | }, 163 | { 164 | "@type":"com.rs.entity.player.skills.Skill", 165 | "type":{ 166 | "combatSkill":false, 167 | "name":"SMITHING" 168 | }, 169 | "level":1, 170 | "maxLevel":1, 171 | "experience":0 172 | }, 173 | { 174 | "@type":"com.rs.entity.player.skills.Skill", 175 | "type":{ 176 | "combatSkill":false, 177 | "name":"MINING" 178 | }, 179 | "level":1, 180 | "maxLevel":1, 181 | "experience":0 182 | }, 183 | { 184 | "@type":"com.rs.entity.player.skills.Skill", 185 | "type":{ 186 | "combatSkill":false, 187 | "name":"HERBLORE" 188 | }, 189 | "level":1, 190 | "maxLevel":1, 191 | "experience":0 192 | }, 193 | { 194 | "@type":"com.rs.entity.player.skills.Skill", 195 | "type":{ 196 | "combatSkill":false, 197 | "name":"AGILITY" 198 | }, 199 | "level":1, 200 | "maxLevel":1, 201 | "experience":0 202 | }, 203 | { 204 | "@type":"com.rs.entity.player.skills.Skill", 205 | "type":{ 206 | "combatSkill":false, 207 | "name":"THIEVING" 208 | }, 209 | "level":1, 210 | "maxLevel":1, 211 | "experience":0 212 | }, 213 | { 214 | "@type":"com.rs.entity.player.skills.Skill", 215 | "type":{ 216 | "combatSkill":false, 217 | "name":"SLAYER" 218 | }, 219 | "level":1, 220 | "maxLevel":1, 221 | "experience":0 222 | }, 223 | { 224 | "@type":"com.rs.entity.player.skills.Skill", 225 | "type":{ 226 | "combatSkill":false, 227 | "name":"FARMING" 228 | }, 229 | "level":1, 230 | "maxLevel":1, 231 | "experience":0 232 | }, 233 | { 234 | "@type":"com.rs.entity.player.skills.Skill", 235 | "type":{ 236 | "combatSkill":false, 237 | "name":"RUNECRAFTING" 238 | }, 239 | "level":1, 240 | "maxLevel":1, 241 | "experience":0 242 | } 243 | ] 244 | } 245 | }, 246 | "gender":0, 247 | "runEnergy":100.0, 248 | "appearance":[ 249 | 18, 250 | 26, 251 | 36, 252 | 0, 253 | 33, 254 | 42, 255 | 10 256 | ], 257 | "colors":[ 258 | 7, 259 | 8, 260 | 9, 261 | 5, 262 | 0 263 | ], 264 | "inventory":[ 265 | -1, 266 | -1, 267 | -1, 268 | -1, 269 | -1, 270 | -1, 271 | -1, 272 | -1, 273 | -1, 274 | -1, 275 | -1, 276 | -1, 277 | -1, 278 | -1, 279 | -1, 280 | -1, 281 | -1, 282 | -1, 283 | -1, 284 | -1, 285 | -1, 286 | -1, 287 | -1, 288 | -1, 289 | -1, 290 | -1, 291 | -1, 292 | -1 293 | ], 294 | "inventoryN":[ 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 0, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 0, 307 | 0, 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 0, 321 | 0, 322 | 0 323 | ], 324 | "equipment":[ 325 | -1, 326 | -1, 327 | -1, 328 | -1, 329 | -1, 330 | -1, 331 | -1, 332 | -1, 333 | -1, 334 | -1, 335 | -1, 336 | -1, 337 | -1, 338 | -1 339 | ], 340 | "equipmentN":[ 341 | 0, 342 | 0, 343 | 0, 344 | 0, 345 | 0, 346 | 0, 347 | 0, 348 | 0, 349 | 0, 350 | 0, 351 | 0, 352 | 0, 353 | 0, 354 | 0 355 | ], 356 | "friends":{ 357 | "@type":"java.util.HashMap" 358 | }, 359 | "ignored":{ 360 | "@type":"java.util.HashMap" 361 | }, 362 | "infractions":{ 363 | "banned":false, 364 | "muted":false, 365 | "banExpirationDate":null, 366 | "muteExpirationDate":null 367 | } 368 | } -------------------------------------------------------------------------------- /src/main/java/com/rs/WorldHandler.java: -------------------------------------------------------------------------------- 1 | package com.rs; 2 | /* 3 | * This file is part of RuneSource. 4 | * 5 | * RuneSource is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * RuneSource is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with RuneSource. If not, see . 17 | */ 18 | 19 | import com.rs.entity.npc.Npc; 20 | import com.rs.entity.npc.NpcUpdating; 21 | import com.rs.entity.player.Player; 22 | import com.rs.entity.player.PlayerUpdating; 23 | import com.rs.plugin.PluginHandler; 24 | import com.rs.task.TaskHandler; 25 | import com.rs.util.Tickable; 26 | 27 | import java.util.concurrent.ExecutorService; 28 | import java.util.concurrent.Executors; 29 | import java.util.concurrent.Phaser; 30 | 31 | /** 32 | * Handles all logged in players. 33 | * 34 | * @author blakeman8192 35 | */ 36 | public final class WorldHandler implements Tickable { 37 | 38 | /** 39 | * Singleton instance. 40 | */ 41 | private static final WorldHandler instance = new WorldHandler(); 42 | 43 | 44 | /** 45 | * All registered players. 46 | */ 47 | private final Player[] players = new Player[2048]; 48 | /** 49 | * All registered NPCs. 50 | */ 51 | private final Npc[] npcs = new Npc[8192]; 52 | 53 | /** 54 | * A thread pool that allows tasks to be executed in parallel. 55 | */ 56 | private final ExecutorService threadPool = Executors.newFixedThreadPool(Server.CPU_CORES); 57 | 58 | /** 59 | * A synchronization barrier that allows a group of threads to wait for each other to 60 | * arrive at a certain point. This is needed so the world can be concurrently updated in phases. 61 | */ 62 | private final Phaser barrier = new Phaser(1); 63 | 64 | private int playerAmount = 0; 65 | private int npcAmount = 0; 66 | 67 | /** 68 | * @return Singleton instance. 69 | */ 70 | 71 | public static WorldHandler getInstance() { 72 | return instance; 73 | } 74 | 75 | /** 76 | * Performs the processing of all world functions. 77 | */ 78 | public void tick() throws Exception { 79 | // XXX: Maybe we could implement loop fusion to speed this up. 80 | 81 | // Perform any logic processing for players. 82 | for (Player player : players) { 83 | if (player == null) { 84 | continue; 85 | } 86 | 87 | try { 88 | player.tick(); 89 | } catch (Exception ex) { 90 | ex.printStackTrace(); 91 | player.disconnect(); 92 | } 93 | } 94 | 95 | // Perform any logic processing for NPCs. 96 | for (Npc npc : npcs) { 97 | if (npc == null) { 98 | continue; 99 | } 100 | 101 | try { 102 | npc.tick(); 103 | } catch (Exception ex) { 104 | ex.printStackTrace(); 105 | unregister(npc); 106 | } 107 | } 108 | 109 | // Process all plugins. 110 | PluginHandler.dispatchTick(); 111 | 112 | // Process all tasks 113 | TaskHandler.tick(); 114 | 115 | // Update all players. 116 | barrier.bulkRegister(playerAmount); 117 | for (Player player : players) { 118 | if (player == null) { 119 | continue; 120 | } 121 | threadPool.execute(() -> { 122 | synchronized (player) { 123 | try { 124 | PlayerUpdating.update(player); 125 | NpcUpdating.update(player); 126 | } catch (Exception ex) { 127 | ex.printStackTrace(); 128 | } finally { 129 | barrier.arriveAndDeregister(); 130 | } 131 | } 132 | }); 133 | 134 | } 135 | barrier.arriveAndAwaitAdvance(); 136 | 137 | 138 | // Reset all players after tick. 139 | for (Player player : players) { 140 | if (player == null) { 141 | continue; 142 | } 143 | 144 | try { 145 | player.reset(); 146 | } catch (Exception ex) { 147 | ex.printStackTrace(); 148 | player.disconnect(); 149 | } 150 | } 151 | 152 | // Reset all NPCs after tick. 153 | for (Npc npc : npcs) { 154 | if (npc == null) { 155 | continue; 156 | } 157 | 158 | try { 159 | npc.reset(); 160 | } catch (Exception ex) { 161 | ex.printStackTrace(); 162 | unregister(npc); 163 | } 164 | } 165 | } 166 | 167 | /** 168 | * Registers a player for processing. 169 | */ 170 | public void register(Player player) { 171 | for (int i = 1; i < players.length; i++) { 172 | if (players[i] == null) { 173 | players[i] = player; 174 | player.setSlot(i); 175 | playerAmount++; 176 | return; 177 | } 178 | } 179 | throw new IllegalStateException("Server is full!"); 180 | } 181 | 182 | /** 183 | * Registers an NPC for processing. 184 | */ 185 | public void register(Npc npc) { 186 | for (int i = 1; i < npcs.length; i++) { 187 | if (npcs[i] == null) { 188 | npcs[i] = npc; 189 | npc.setSlot(i); 190 | npcAmount++; 191 | return; 192 | } 193 | } 194 | throw new IllegalStateException("Server is full!"); 195 | } 196 | 197 | /** 198 | * Unregisters a player from processing. 199 | */ 200 | public void unregister(Player player) { 201 | if (player.getSlot() == -1) { 202 | return; 203 | } 204 | players[player.getSlot()] = null; 205 | playerAmount--; 206 | } 207 | 208 | /** 209 | * Unregisters an NPC from processing. 210 | */ 211 | public void unregister(Npc npc) { 212 | if (npc.getSlot() == -1) { 213 | return; 214 | } 215 | npcs[npc.getSlot()] = null; 216 | npcAmount--; 217 | } 218 | 219 | public boolean isPlayerOnline(String username) { 220 | if (playerAmount == 0) 221 | return false; 222 | 223 | for (Player player : players) { 224 | if (player == null) 225 | continue; 226 | 227 | if (player.getAttributes().getUsername().equals(username)) { 228 | return true; 229 | } 230 | } 231 | return false; 232 | } 233 | 234 | public Player getPlayer(String username) { 235 | if (playerAmount == 0) 236 | throw new IndexOutOfBoundsException(); 237 | 238 | for (Player player : players) { 239 | if (player == null) 240 | continue; 241 | 242 | if (player.getAttributes().getUsername().equals(username)) { 243 | return player; 244 | } 245 | } 246 | throw new IndexOutOfBoundsException(); 247 | } 248 | 249 | public static void trySendMessage(String username, String message) { 250 | try { 251 | Player p = WorldHandler.getInstance().getPlayer(username); 252 | p.sendMessage(message); 253 | } catch (IndexOutOfBoundsException e) { 254 | } 255 | } 256 | 257 | /** 258 | * @return the amount of online players 259 | */ 260 | public int playerAmount() { 261 | return playerAmount; 262 | } 263 | 264 | /** 265 | * @return the amount of online NPCs 266 | */ 267 | public int npcAmount() { 268 | return npcAmount; 269 | } 270 | 271 | /** 272 | * Gets all registered players. 273 | */ 274 | public Player[] getPlayers() { 275 | return players; 276 | } 277 | 278 | /** 279 | * Gets all registered NPCs. 280 | */ 281 | public Npc[] getNpcs() { 282 | return npcs; 283 | } 284 | 285 | } 286 | -------------------------------------------------------------------------------- /plugins/scripts/Infractions.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of RuneSource. 3 | * 4 | * RuneSource is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * RuneSource is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with RuneSource. If not, see . 16 | */ 17 | import com.rs.Server 18 | import com.rs.WorldHandler 19 | import com.rs.entity.player.Player 20 | import com.rs.entity.player.PlayerAttributes 21 | 22 | import com.rs.entity.player.infractions.PlayerInfractions 23 | 24 | import com.rs.plugin.event.CommandEvent 25 | import com.rs.plugin.event.PlayerLoggedInEvent 26 | import com.rs.plugin.event.PlayerLoggedOutEvent 27 | import com.rs.plugin.listener.CommandListener 28 | import com.rs.plugin.listener.PlayerConnectivityListener 29 | 30 | import java.text.DateFormat 31 | import java.text.SimpleDateFormat 32 | 33 | class Infractions implements PlayerConnectivityListener, CommandListener { 34 | 35 | File logFile = new File("./data/logs/infractions.log") 36 | DateFormat dateFormat = new SimpleDateFormat(Server.getInstance().getSettings().getDateFormat()) 37 | PlayerInfractionCommand[] commands = [ 38 | new PlayerInfractionCommand("mute", Player.Privilege.MODERATOR, true) { 39 | @Override 40 | void apply(Player target, PlayerInfractions infractions, Date expirationDate) { 41 | infractions.setMuted(true) 42 | infractions.setMuteExpirationDate(expirationDate) 43 | 44 | if (target != null) { 45 | target.sendMessage("You have been muted, it will expire: " + infractions.muteExpiration()) 46 | } 47 | } 48 | }, 49 | new PlayerInfractionCommand("unmute", Player.Privilege.MODERATOR, false) { 50 | @Override 51 | void apply(Player target, PlayerInfractions infractions, Date expirationDate) { 52 | infractions.setMuted(false) 53 | infractions.setMuteExpirationDate(expirationDate) 54 | 55 | if (target != null) { 56 | target.sendMessage("You have been unmuted.") 57 | } 58 | } 59 | }, 60 | new PlayerInfractionCommand("ban", Player.Privilege.MODERATOR, true) { 61 | @Override 62 | void apply(Player target, PlayerInfractions infractions, Date expirationDate) { 63 | infractions.setBanned(true) 64 | infractions.setBanExpirationDate(expirationDate) 65 | 66 | if (target != null) { 67 | target.disconnect() 68 | } 69 | } 70 | }, 71 | new PlayerInfractionCommand("unban", Player.Privilege.MODERATOR, false) { 72 | @Override 73 | void apply(Player target, PlayerInfractions infractions, Date expirationDate) { 74 | infractions.setBanned(false) 75 | infractions.setBanExpirationDate(expirationDate) 76 | } 77 | } 78 | ] 79 | 80 | void logIn(PlayerLoggedInEvent evt) { 81 | Player player = evt.getPlayer() 82 | PlayerInfractions infractions = player.getAttributes().getInfractions() 83 | 84 | if (infractions.isMuted()) { 85 | player.sendMessage("Your mute will expire: " + infractions.muteExpiration()) 86 | } 87 | } 88 | 89 | void logOut(PlayerLoggedOutEvent evt) { 90 | } 91 | 92 | void command(CommandEvent evt) { 93 | Player player = evt.getPlayer() 94 | PlayerAttributes attributes = player.getAttributes() 95 | String commandName = evt.getCommandName() 96 | String[] args = evt.getArgs() 97 | 98 | // Map commandName to PlayerInfractionCommand 99 | PlayerInfractionCommand cmd 100 | 101 | try { 102 | cmd = command(commandName) 103 | } catch (IndexOutOfBoundsException ignored) { 104 | return 105 | } 106 | 107 | // Validate args 108 | Date expirationDate 109 | 110 | if (cmd.isTimedInfraction) { 111 | if (args.length < 2) { 112 | player.sendMessage("Syntax: ::$commandName [username] [expiration date, or 'never']") 113 | return 114 | } 115 | String expirationDateStr = join(" ", args, 1) 116 | expirationDate = (expirationDateStr == "never") ? null : dateFormat.parse(expirationDateStr) 117 | } else { 118 | if (args.length < 1) { 119 | player.sendMessage("Syntax: ::$commandName [username]") 120 | return 121 | } 122 | } 123 | 124 | // Apply infraction 125 | String targetUsername = args[0] 126 | Date applicationDate = new Date() 127 | PlayerAttributes targetAttributes 128 | 129 | try { // check online players 130 | Player targetPlayer = WorldHandler.getInstance().getPlayer(targetUsername) 131 | targetAttributes = targetPlayer.getAttributes() 132 | infract(targetPlayer, targetAttributes, attributes, cmd, applicationDate, expirationDate) 133 | } catch (IndexOutOfBoundsException e) { // load from file 134 | try { 135 | targetAttributes = Server.getInstance().getPlayerFileHandler().load(targetUsername) 136 | infract(null, targetAttributes, attributes, cmd, applicationDate, expirationDate) 137 | Server.getInstance().getPlayerFileHandler().save(targetAttributes) 138 | } catch (Exception ex) { 139 | player.sendMessage("player with username '$username' does not exist") 140 | } 141 | } 142 | } 143 | 144 | void infract(Player targetPlayer, PlayerAttributes targetAttributes, PlayerAttributes authorAttributes, 145 | PlayerInfractionCommand cmd, Date applicationDate, Date expirationDate) { 146 | // Check comparative privileges 147 | if (targetAttributes.getPrivilege().gte(authorAttributes.getPrivilege())) { 148 | player.sendMessage("You cannot infract another player with the same, or greater privilege than you") 149 | return 150 | } 151 | 152 | // Apply infraction 153 | PlayerInfractions infractions = targetAttributes.getInfractions() 154 | cmd.apply(targetPlayer, infractions, expirationDate) 155 | 156 | // Log 157 | log(targetAttributes.getUsername(), authorAttributes.getUsername(), cmd, applicationDate, expirationDate) 158 | } 159 | 160 | void log(String target, String author, PlayerInfractionCommand cmd, Date applicationDate, Date expirationDate) { 161 | String appDate = dateFormat.format(applicationDate) 162 | String expData = expirationDate == null ? "never" : dateFormat.format(expirationDate) 163 | String operation = cmd.commandName 164 | String log = "[$appDate]: [$author -> $target] $operation expiry=$expData" 165 | 166 | FileWriter writer = new FileWriter(logFile, true) 167 | writer.write(log + System.lineSeparator()) 168 | writer.flush() 169 | writer.close() 170 | } 171 | 172 | PlayerInfractionCommand command(String commandName) { 173 | for (PlayerInfractionCommand pic : commands) { 174 | if (pic.getCommandName().equalsIgnoreCase(commandName)) { 175 | return pic 176 | } 177 | } 178 | throw new IndexOutOfBoundsException() 179 | } 180 | 181 | String join(String delimeter, String[] collection, int startIdx) { 182 | StringBuilder builder = new StringBuilder() 183 | 184 | for (int i = startIdx; i < collection.length; i++) { 185 | builder.append(collection[i]) 186 | 187 | if (i + 1 < collection.length) 188 | builder.append(delimeter) 189 | } 190 | 191 | return builder.toString() 192 | } 193 | 194 | abstract class PlayerInfractionCommand { 195 | 196 | String commandName 197 | Player.Privilege privilege 198 | boolean isTimedInfraction 199 | 200 | PlayerInfractionCommand(String commandName, Player.Privilege privilege, boolean isTimedInfraction) { 201 | this.commandName = commandName 202 | this.privilege = privilege 203 | this.isTimedInfraction = isTimedInfraction 204 | } 205 | 206 | abstract void apply(Player target, PlayerInfractions targetAttributes, Date expirationDate); 207 | } 208 | } 209 | --------------------------------------------------------------------------------