Skip to content

Commit

Permalink
More settings and command fixes (#579)
Browse files Browse the repository at this point in the history
* Add aggression toggle

* Disable world messages by default

* Simplify xp rate calls

* Add player file auto saving every x minutes

* Add more details to dev setup guide

* Update obj spawn command

* Remove unused coroutine scope
  • Loading branch information
GregHib authored Jan 12, 2025
1 parent 0c6ea81 commit a41dd53
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 38 deletions.
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,37 @@ It is recommended to use IntelliJ IDEA to develop with Void.
The community edition can be downloaded for free from the [jetbrains website.](https://www.jetbrains.com/idea/download/)
See [the installation guide](https://www.jetbrains.com/help/idea/installation-guide.html) for more instructions.

Once inside the IDE, you can create a new project by going to `File | New | Project from version control... |`
Once opened the IDE click the `Clone Repository` button or `File | New | Project from version control... |` if in the full application.

Selecting `git` version control and entering the void project URL `[email protected]:GregHib/void.git` found under the `<> Code` button on the [GitHub page](https://github.com/GregHib/void).
Selecting `git` version control and entering the void project URL Found under the `<> Code` button on the [GitHub page](https://github.com/GregHib/void).
- `[email protected]:GregHib/void.git` if you have [GitHub authentication setup](https://docs.github.com/en/authentication).
- `https://github.com/GregHib/void.git` if you don't have SSH authentication.

Press clone and after a little while the project will be opened for you, the JDK indexed and gradle setup.
> [!NOTE]
> When git is not installed it will display an error and the option to "Download and install", click this and retry the previous step.
> Click "Trust Project" if also asked.
Press "clone" and after the download is complete the project will be opened for you.

Under `Project Structure... | Project` settings set `SDK` to JDk 19+ (download as needed) and let it index.

Run the following command in the terminal to set up Gradle or close and re-open the project to trigger IntelliJ's `Open as Gradle Project` popup.

```bash
./gradlew build -x test
```

Extract the [cache files](https://mega.nz/folder/ZMN2AQaZ#4rJgfzbVW0_mWsr1oPLh1A) into a new directory called `/cache/` inside of the `/data/` directory.

From here you can navigate in the left panel to `/game/src/main/kotlin/world/gregs/voidps/` where you will find [Main.kt](./game/src/main/kotlin/world/gregs/voidps/Main.kt) which you should be able to right-click and run.
From here you can navigate in the left panel to `/game/src/main/kotlin/world/gregs/voidps/` (Or Ctrl/Cmd + N for class search) where you will find [Main.kt](./game/src/main/kotlin/world/gregs/voidps/Main.kt) which you should be able to right-click and run.

You can also run in the command line using the gradle wrapper.

```bash
./gradlew run
```

Once the server is up and running; setup the [void-client repository](https://github.com/GregHib/void-client/) or download one of the [prebuilt client.jars](https://github.com/GregHib/void-client/releases) and run to log into the game.
Once the server is up and running; download one of the [prebuilt client.jars](https://github.com/GregHib/void-client/releases) or set up the [void-client repository](https://github.com/GregHib/void-client/) and run to log into the game.

Don't forget to check out our [Contributing guidelines](./CONTRIBUTING.md) before submitting your first pull request!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class PlayerAccountLoader(
client.disconnect(Response.ACCOUNT_ONLINE)
return null
}
val player = storage.load(username)?.toPlayer(Settings["world.experienceRate", 1.0]) ?: accounts.create(username, passwordHash)
val player = storage.load(username)?.toPlayer() ?: accounts.create(username, passwordHash)
logger.info { "Player $username loaded and queued for login." }
connect(player, client, displayMode)
return player.instructions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class AccountManager(
get() = Tile(Settings["world.home.x", 0], Settings["world.home.y", 0], Settings["world.home.level", 0])

fun create(name: String, passwordHash: String): Player {
return Player(tile = homeTile, accountName = name, passwordHash = passwordHash, experience = Experience(rate = Settings["world.experienceRate", 1.0])).apply {
return Player(tile = homeTile, accountName = name, passwordHash = passwordHash).apply {
this["creation"] = System.currentTimeMillis()
this["new_player"] = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ data class PlayerSave(
val ignores: List<String>
) {

fun toPlayer(rate: Double): Player {
fun toPlayer(): Player {
return Player(
accountName = name,
passwordHash = password,
tile = tile,
experience = Experience(experience, blocked.toMutableSet(), rate = rate),
experience = Experience(experience, blocked.toMutableSet()),
levels = Levels(levels),
body = BodyParts(male, looks, colours),
variables = variables.toMutableMap(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ internal class PlayerYamlReaderConfig : YamlReaderConfiguration() {
val exp = Experience(
experience = (value["experience"] as List<Double>).toDoubleArray(),
blocked = (value["blocked"] as List<Skill>).toMutableSet(),
rate = Settings["world.experienceRate", 1.0]
)
super.set(map, key, exp, indent, parentMap)
} else if (key == "levels") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package world.gregs.voidps.engine.entity.character.player.skill.exp

import world.gregs.voidps.engine.data.Settings
import world.gregs.voidps.engine.entity.character.player.Player
import world.gregs.voidps.engine.entity.character.player.skill.Skill
import world.gregs.voidps.engine.entity.character.player.skill.level.Level
Expand All @@ -8,8 +9,7 @@ import world.gregs.voidps.engine.event.EventDispatcher
class Experience(
val experience: DoubleArray = defaultExperience.clone(),
val blocked: MutableSet<Skill> = mutableSetOf(),
private val maximum: Double = MAXIMUM_EXPERIENCE,
private val rate: Double = DEFAULT_EXPERIENCE_RATE
private val maximum: Double = MAXIMUM_EXPERIENCE
) {

lateinit var events: EventDispatcher
Expand All @@ -36,10 +36,10 @@ class Experience(
return
}
if (blocked.contains(skill)) {
events.emit(BlockedExperience(skill, experience * rate))
events.emit(BlockedExperience(skill, experience * Settings["world.experienceRate", DEFAULT_EXPERIENCE_RATE]))
} else {
val current = get(skill)
set(skill, current + experience * rate)
set(skill, current + experience * Settings["world.experienceRate", DEFAULT_EXPERIENCE_RATE])
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import io.mockk.verify
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import world.gregs.voidps.engine.data.Settings
import world.gregs.voidps.engine.entity.character.player.Player
import world.gregs.voidps.engine.entity.character.player.skill.exp.BlockedExperience
import world.gregs.voidps.engine.entity.character.player.skill.exp.Experience
import world.gregs.voidps.engine.entity.character.player.skill.exp.Experience.Companion.DEFAULT_EXPERIENCE_RATE
import world.gregs.voidps.engine.entity.character.player.skill.exp.GrantExp
import world.gregs.voidps.engine.entity.character.player.skill.exp.exp
import world.gregs.voidps.engine.event.EventDispatcher
Expand Down Expand Up @@ -48,11 +50,13 @@ internal class ExperienceTableTest {

@Test
fun `Add experience with 10x rate`() {
experience = Experience(maximum = 500.0, rate = 10.0)
Settings.load(mapOf("world.experienceRate" to "10.0"))
experience = Experience(maximum = 500.0)
experience.events = events
experience.add(Skill.Attack, 10.0)
experience.add(Skill.Attack, 10.0)
assertEquals(200.0, experience.get(Skill.Attack))
Settings.clear()
}

@Test
Expand Down
4 changes: 1 addition & 3 deletions game/src/main/kotlin/world/gregs/voidps/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@ import world.gregs.voidps.network.LoginServer
import world.gregs.voidps.network.login.protocol.decoders
import world.gregs.voidps.script.loadScripts
import java.util.*
import kotlin.coroutines.CoroutineContext

/**
* @author GregHib <[email protected]>
* @since April 18, 2020
*/
object Main : CoroutineScope {
object Main {

override val coroutineContext: CoroutineContext = Contexts.Game
private val logger = InlineLogger()

@OptIn(ExperimentalUnsignedTypes::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,22 +274,12 @@ adminCommand("sendItems") {
player.sendInventoryItems(90, 28, ags, true)
}

adminCommand("obj (object-id) [object-type] [object-rotation]", "spawn an object") {
if (content.isNotBlank()) {
val parts = content.split(" ")
val id = parts.getOrNull(0)
if (id != null) {
val rotation = parts.getOrNull(1)?.toIntOrNull() ?: 0
objects.add(id, player.tile.addY(1), 0, rotation, 10)
objects.add(id, player.tile.addY(1), 10, rotation, 10)
objects.add(id, player.tile.addY(1), 22, rotation, 10)
}
} else {
val objs = get<GameObjects>()
objs[player.tile].forEach {
println(it.intId)
}
}
adminCommand("obj (object-id) [object-shape] [object-rotation]", "spawn an object") {
val parts = content.split(" ")
val id = parts[0]
val shape = parts.getOrNull(1)?.toIntOrNull() ?: 10
val rotation = parts.getOrNull(2)?.toIntOrNull() ?: 0
objects.add(id, player.tile, shape, rotation)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import world.gregs.voidps.engine.entity.character.npc.hunt.huntPlayer
import world.gregs.voidps.engine.entity.character.player.PlayerOption

huntPlayer(mode = "aggressive") { npc ->
if (attacking(npc, target)) {
if (!Settings["world.npcs.aggression", true] || attacking(npc, target)) {
return@huntPlayer
}
if (Settings["world.npcs.safeZone", false] && npc.tile.region.id == 12850) {
Expand All @@ -21,7 +21,7 @@ huntPlayer(mode = "aggressive") { npc ->
}

huntPlayer(mode = "cowardly") { npc ->
if (attacking(npc, target)) {
if (!Settings["world.npcs.aggression", true] || attacking(npc, target)) {
return@huntPlayer
}
npc.mode = Interact(npc, target, PlayerOption(npc, target, "Attack"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package world.gregs.voidps.world.interact.entity.player

import world.gregs.voidps.engine.data.SaveQueue
import world.gregs.voidps.engine.data.Settings
import world.gregs.voidps.engine.data.settingsReload
import world.gregs.voidps.engine.entity.World
import world.gregs.voidps.engine.entity.character.player.Players
import world.gregs.voidps.engine.entity.worldSpawn
import world.gregs.voidps.engine.inject
import world.gregs.voidps.engine.timer.toTicks
import java.util.concurrent.TimeUnit

val players: Players by inject()
val saveQueue: SaveQueue by inject()

worldSpawn {
autoSave()
}

settingsReload {
val minutes = Settings["storage.autoSave.minutes", 0]
if (World.contains("auto_save") && minutes <= 0) {
World.clearQueue("auto_save")
} else if (!World.contains("auto_save") && minutes > 0) {
autoSave()
}
}

fun autoSave() {
val minutes = Settings["storage.autoSave.minutes", 0]
if (minutes <= 0) {
return
}
World.queue("auto_save", TimeUnit.MINUTES.toTicks(minutes)) {
for (player in players) {
saveQueue.save(player)
}
autoSave()
}
}
13 changes: 11 additions & 2 deletions game/src/main/resources/game.properties
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@ world.home.y=3219
#world.home.level=0

# Whether content should broadcast global messages
world.messages=true
world.messages=false

# Experience multiplier (1.0 = normal XP rate)
world.experienceRate=1.0

# Experience multiplier (1.0 = normal XP rate)
world.saveEveryMinutes=1

#------- Player Rules -------

# The maximum number of players that can be online at once
Expand All @@ -89,6 +92,9 @@ world.npcs.randomWalk=true
# Whether Lumbridge is an NPC aggression-free area
world.npcs.safeZone=true

# Whether NPCs can attack players
world.npcs.aggression=true


#===================================
# Gameplay Mechanics
Expand Down Expand Up @@ -140,7 +146,7 @@ events.shootingStars.maxRespawnTimeMinutes=120
#===================================

# The number of AI-controlled bots spawned on startup
bots.count=10
bots.count=0

# What tasks to give AI-controlled bots with no tasks (options: nothing, randomWalk)
bots.idle=randomWalk
Expand All @@ -153,6 +159,9 @@ bots.idle=randomWalk
# The type of storage backend (options: files, database)
storage.type=files

# How frequently to save players files (0 to only save on logout)
storage.autoSave.minutes=5

# The directory where player save files are stored
storage.players.path=./data/saves/

Expand Down

0 comments on commit a41dd53

Please sign in to comment.