Code Monkey home page Code Monkey logo

luna's Introduction

Luna | Build Status Discord chat GitHub license

Luna is a Runescape emulator that aims to correct all of the fundamental issues accumulated over the various releases of it's predecessor Asteria. This project is no longer in development.

Some of the sweetness within Luna

  • high performing and scalable code where it matters most
  • lightweight abstraction models that avoid featuritis
  • effective management of the host computer's resources
  • flexible, event-based Kotlin plugin system for game content
  • highly documented code

Goals

Currently, the Runescape private server scene is plagued with somewhat of a dilemma. One half of servers are lightweight, easy to understand, but perform poorly; while the other half perform very well, but contain a lot of bloat and overcomplicated code.

The goal of Luna (and its predecessor, Asteria) is to rectify this by being fast and scalable, while still being relatively lightweight and easy to understand.

Getting started

Luna is currently unstable and therefore not ready to be used in a production environment. The first stable pre-release will be v0.5, which is planned for sometime in February, 2019.

An official client can be found here, along with the cache. To learn more about how to get started with Luna please visit the wiki.

For more help, inquiries, or just to dicuss the state of the project please join our Discord server.

Acknowledgements

Much of the inspiration for various features within Luna came from

luna's People

Contributors

asharma-gh avatar colton-bouchard avatar dependabot[bot] avatar dotmbf avatar dukecharles avatar eschouten avatar jhg023 avatar keotl avatar lare96 avatar mikeysasse avatar natis1 avatar notjuanortiz avatar searledan avatar tlf30 avatar vl1 avatar zeruth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

luna's Issues

Dependencies not included

Can you push the dependencies to the project? Would save others a lot of time getting set up.
Also is there a way to contact you in order to discuss other features and design?

Missing classes

Hi,

This looks like a very interesting project and I would like to contribute. Could you make the repo build again?

Thanks!

Add support for weight

It's already implemented within the run energy algorithm but I need to add support for items in your inventory and equipment updating your weight value

Remove magic numbers (in item-related contexts)

Create a new constructor for item, with signature: Item(String name, int amount) which will then use ItemDefinition#getItem(String name) to set its ID. This will result in less magic numbers.

edit: i.e. in plugins/plugin/player/starter_package.scala

Support for shops

Blocked by issue #30

  • Each shop should have its own .TOML file
  • Only physical currencies should be supported
  • Restocking should be done lazily
  • The players who currently have the shop open should be able to be retrieved cheaply

Add support for all doors, gates, etc.

I think this might be related to entities not showing up, but I could be dead wrong about that. The clients sends the command to open the door to Luna, but the door does not open. If this needs to be done in a Scala plugin, let me know and I might fiddle around with learning how to write one.

Migrate from snakeYAML to Boon Json until Toml4j has serialization support

Boon is the highest performing JSON library out there right now and follows a design similar to google-gson. SnakeYAML is a PITA to use, terribly designed, and with that I've come to the conclusion that it's just not worth using.

I've been looking into TOML lately and I like it but toml4j is the only library available for it and doesn't have serialization support. So I might be changing the data serialization format to TOML in the future when it does support serialization.

Clean up event interception

The Event interception looks wonky. This is partly due to syntactical limitations, but I still think we can do better. Improvements to it include

  • Merging intercept/intercept_@ into one "intercept" method (only possible with parameter defined events)
  • Rename "intercept" to "on"

The result would be event interception looking something like the following examples

on('button_click, 1, 2, 3, 4) { msg =>
  ...
}

on('button_click) { msg =>
  ...
}

Or

on(ButtonClickEvent.is, 1, 2, 3, 4) { msg =>
  ...
}

on(ButtonClickEvent.is) { msg =>
  ...
}

You could even use pattern matching to get around the "parameter defined events" issue

on[ButtonClickEvent] match {
  case 1 | 2 | 3 => println("Clicked button id #1, #2, or #3")
  case 4 => println("Clicked button id #4")
  case 5 => prinlnt("Clicked button id #5")
}

But I'm not really sure about this syntax because it isn't all that compact when you're only dealing with a single value

on[ButtonClickEvent] match {
  case 1 => println("Clicked button id #1")
}

A good compromise would be to allow the compact syntax for 1-5 values, but any more than that and pattern matching must be used.

GUI to manage activated/deactivated plugins

The ability to do something like "::hotfix login.scala" would provide many benefits. Why re-interpret potentially a hundred plugins just to update the 1, 2, or 3 plugins you modified?

I was thinking about how this could be done and I think it could be done by making the EventListeners hold the following data

  • the biconsumer, basically the code within the listener (already present)
  • the name of the plugin file this eventlistener came from
  • the event listeners index in its pipeline

Then the appropriate old EventListener could be swapped out for the new one. If that can't be done, a good compromise would be to just reload the pipelines of all the event types that the plugin file listens for.

Interface abstraction model

Interface

Superclass of all interfaces, contains functions that will be invoked by InterfaceSet when necessary.

  • open(Player) : void
  • close(Player) : void
  • closeOnAction : boolean
  • closeOnWalk : boolean

InterfaceType

An enumerated type describing each type of interface.

  • STATIC

A standard interface; closes upon movement and action initialization (ie. smithing, skill guides).

  • STATIC_INVENTORY

Similar to STATIC, but overlays the inventory with an interface as well (ie. banking, trading).

  • DYNAMIC

An interface that doesn't close upon movement and action initialization. These types of interfaces usually do not take up big portions of the screen (ie. wilderness skull and level, tutorial island progress).

  • DIALOGUE

An interface displayed on the chatbox. May or may not close on movement and action initialization (ie. cooking dialogue, npc/player dialogues).

  • TAB

An interface displayed on a sidebar tab (ie. ancient spellbook, music player).

InterfaceSet

Manages the currently open interface.

  • Only one interface of a specific InterfaceType can be open at a time
  • Use EnumMap to represent the data
  • STATIC interface types cannot be open while STATIC_INVENTORY types are open

Mobs don't appear to players

When testing punishment commands recently, I noticed that mobs don't appear to players despite being in the same region. I haven't debugged or looked into this at all yet, but I assume it has something to do with this

Choosing the right scripting language

A discussion needs to be had about the current implications of continuing to use Scala as the content scripting language. I love Scala, but as it stands right now

  • The scripting system is currently faced with a bug from the Scala compiler which has rendered the server "unstartable"
  • Scala was never meant to be used as a large scale scripting language
  • JSR-223 compliance was a community contribution, which means support is very limited

So I think that a change of languages might be in order to preserve the longevity of Luna. I've come up with a basic list describing what a language implementation needs in order to be the 'perfect' scripting language for this project

  • Monkey patching
  • Flexible anonymous function syntax
  • Java compatible
  • JSR-223 compliant

I was thinking of using maybe Lua or Ruby. Anyone have any other suggestions on what would be a good replacement language?

Data structures that hold definitions should be immutable

https://github.com/lare96/luna/tree/master/src/main/java/io/luna/game/model/def

using mutable arrays/maps doesn't seem like a good way to go about things

a good solution would be to add a static method to those classes called setDefinitons, the parsers would build the collections and the setDefinitons method would create an immutable copy of them. subsequent invocation attempts once the immutable collection has already been created will result in an IllegalStateException being thrown

setDefinitons should also be synchronized

Update to Scala 2.12.1

As of now, plugins are currently written in a Scala 2.11.8 DSL. Scala 2.12 brought Java 8 compatibility which aside from performance enhancements, allows for Java functional interfaces to be expressed as lambdas within Scala.

This nullifies the need for 'hacky' implicit conversions, which should be removed as this update is applied.

Dynamically reloading plugins, or "hotfixing"

Plugins are interpreted on startup and wrapped inside EventListeners, which are then added to EventListenerPipelines. Those pipelines are notified of any Events which are then subsequently sent through the pipeline to be intercepted by each listener (unless the event is terminated by one of the listeners before the traversal can complete).

With that said, all that would have to be done would be to discard all pipelines and re-interpret all of the plugins which would then reconstruct the pipelines with the newest plugin code.

Last time I tried it, there seemed to be some sort of classpath issue with the Scala compiler/interpreter. I never really got around to looking into it, but this is a feature that I really want added in the near future.

Task/Action system

I want the main Task system to just be a simple, generic, cycle based task... nothing special, nothing to do with the Player

I don't want Action's to be an extension of Task... that will make the design look inconsistent and hard to follow (being able to schedule Action's within the "TaskManager" ???). Rather than extending Task, imposition should be used where needed

Action's should have a set of "traits" (EnumSet could be used be of it's high performance?) that define how it functions... ex.

  • ONE_INSTANCE (only one of these actions are allowed to be running at a time per player)
  • STOP_ON_WALK (self-explanitory)
  • STOP_ON_DROP (^)
  • ... etc.

Could also have specialized Action's that automatically include common traits like "FixedAction" (bad name) that automatically includes all of the STOP_ON_WALK, DROP, EQUIP, etc. traits...

so all in all, the hierarchy should look something like this

io.luna.game.task

  • Task (abstract class)
  • TaskManager (final class, instantiated within the World class, or LunaContext)

io.luna.game.action

  • Action
    • RecurringAction
      • DistancedAction
  • ActionQueue
  • ActionTrait

Equipment definitions

Needed in order to finish off the Equipment class. I might need to dump additional data from the wiki as well in order to accomplish this.

Add dialogue support

Will just use a queue to hold the dialogue objects in order, and poll them when the next dialogue is needed. The only problem is that options can get very ugly when you have a complicated dialogue. Can't really think of anything to counter that issue at the moment

Official Client

Luna doesn't have an official client, or even an unofficial one as far as I know. What do you guys use to run alongside the server?

Hotfixing is not thread safe

Hotfixing works by asynchronously reloading plugins into a new pipeline set, and then replacing the currently used pipeline set with it.

Replacing the currently used pipeline set must happen on the game thread to ensure thread safety.

Consider changing the intercept/intercept at (>>/>>@) functions

A well seasoned Java/Scala programmer that sees ">>" is going to assume it's a bitwise operation. Therefore, I don't think that this should be associated with event interception.

Some better solutions to look into

  • intercept/intercept_@
  • intercept/interceptAt
  • on/onMatch
  • ->/->@

Standardize plugin errors

Errors thrown by plugins are objectively a complete and total eyesore. They're unnecessarily bloated, and can look vastly different from one another depending on when the error is thrown (evaluation or application). Take for example, the following two different types of errors

Application error

21 Jul 2017 18:45:07 io.luna.game.event.EventListener [LunaGameThread RUNNING]
ERROR: Catching
io.luna.game.plugin.PluginFailureException: 2570 (of class java.lang.Integer)
	at io.luna.game.event.EventListener.apply(EventListener.java:57) ~[classes/:?]
	at io.luna.game.event.EventListenerPipeline.traverse(EventListenerPipeline.java:59) ~[classes/:?]
	at io.luna.game.plugin.PluginManager.post(PluginManager.java:43) ~[classes/:?]
	at io.luna.game.model.item.Equipment$EquipmentListener.sendEvent(Equipment.java:77) ~[classes/:?]
	at io.luna.game.model.item.Equipment$EquipmentListener.onBulkUpdate(Equipment.java:62) ~[classes/:?]
	at io.luna.game.model.item.ItemContainer.fireUpdateEvent(ItemContainer.java:832) ~[classes/:?]
	at io.luna.game.model.item.ItemContainer.set(ItemContainer.java:717) ~[classes/:?]
	at io.luna.game.model.item.Equipment.add(Equipment.java:263) ~[classes/:?]
	at io.luna.game.model.item.ItemContainer.addAll(ItemContainer.java:248) ~[classes/:?]
	at $line40.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$1(<console>:205) ~[?:?]
	at $line40.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$1$adapted(<console>:199) ~[?:?]
	at $line5.$read$$iw$$iw$$anon$1.accept(<console>:185) ~[?:?]
	at io.luna.game.event.EventListener.apply(EventListener.java:49) ~[classes/:?]
	at io.luna.game.event.EventListenerPipeline.traverse(EventListenerPipeline.java:59) ~[classes/:?]
	at io.luna.game.plugin.PluginManager.post(PluginManager.java:43) ~[classes/:?]
	at io.luna.game.model.mobile.Player.onActive(Player.java:279) ~[classes/:?]
	at io.luna.game.model.Entity.setState(Entity.java:139) ~[classes/:?]
	at io.luna.game.model.mobile.MobList.add(MobList.java:195) ~[classes/:?]
	at io.luna.game.model.World.dequeueLogins(World.java:96) ~[classes/:?]
	at io.luna.game.GameService.runOneIteration(GameService.java:78) ~[classes/:?]
	at com.google.common.util.concurrent.AbstractScheduledService$ServiceDelegate$Task.run(AbstractScheduledService.java:188) ~[guava-22.0.jar:?]
	at com.google.common.util.concurrent.Callables$4.run(Callables.java:122) ~[guava-22.0.jar:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_131]
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) ~[?:1.8.0_131]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) ~[?:1.8.0_131]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) ~[?:1.8.0_131]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_131]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_131]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
Caused by: scala.MatchError: 2570 (of class java.lang.Integer)
	at $line47.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$2(<console>:211) ~[?:?]
	at scala.runtime.java8.JFunction1$mcZI$sp.apply(JFunction1$mcZI$sp.java:12) ~[scala-library-2.12.2.jar:?]
	at scala.Option.foreach(Option.scala:257) ~[scala-library-2.12.2.jar:?]
	at $line47.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$1(<console>:209) ~[?:?]
	at $line47.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.$anonfun$new$1$adapted(<console>:206) ~[?:?]
	at $line5.$read$$iw$$iw$$anon$1.accept(<console>:185) ~[?:?]
	at io.luna.game.event.EventListener.apply(EventListener.java:49) ~[classes/:?]
	... 28 more

Evaluation error

21 Jul 2017 20:33:50 io.luna.game.plugin.PluginBootstrap [LunaInitializationThread]
ERROR: Catching
javax.script.ScriptException: unbound placeholder parameter in import io.luna.game.event.Event
import io.luna.game.event.impl.{ButtonClickEvent, EquipmentChangeEvent}
import io.luna.game.model.item.Equipment.RING
import io.luna.game.model.mobile.Player


private val RING_OF_STONE = 6583
private val STONE_MORPH = 2626

private val EASTER_RING = 7927
private val EGGS_MORPH = Vector(3689, 3690, 3691, 3692, 3693, 3694)


private def morph(plr: Player, msg: Event, to: Int) = {
  (0 to 13).filterNot(_ == 3).foreach(plr.sendTabInterface(_, -1))
  plr.sendForceTab(3)
  plr.sendTabInterface(3, 6014)

  plr.lockMovement
  plr.transform(to)
  msg.terminate
}

private def unmorph(plr: Player) = {
  if (plr.inventory.computeRemainingSize > 1) {
    plr.equipment.unequip(RING)

    plr.displayTabInterfaces()

    plr.unlockMovement
    plr.transform(-1)
  } else {
    plr.sendMessage("You do not have enough space in your inventory.")
  }
}


on[EquipmentChangeEvent] { msg =>
  if (msg.index == RING) {

    msg.newId.foreach {
      case RING_OF_STONE => morph(msg.plr, msg, STONE_MORPH)
      case EASTER_RING => morph(msg.plr, msg, rand(EGGS_MORPH))
      case _ => _
    }
  }
}

// unmorph (figure out proper id)
onargs[ButtonClickEvent](23132) { msg => unmorph(msg.plr) } at line number 44 at column number 17
	at scala.tools.nsc.interpreter.Scripted.$anonfun$compile$2(Scripted.scala:163) ~[scala-compiler-2.12.2.jar:?]
	at scala.tools.nsc.interpreter.Scripted.$anonfun$compile$1(Scripted.scala:162) ~[scala-compiler-2.12.2.jar:?]
	at scala.tools.nsc.interpreter.Scripted.withCompileContext(Scripted.scala:134) ~[scala-compiler-2.12.2.jar:?]
	at scala.tools.nsc.interpreter.Scripted.compile(Scripted.scala:148) ~[scala-compiler-2.12.2.jar:?]
	at scala.tools.nsc.interpreter.Scripted.eval(Scripted.scala:181) ~[scala-compiler-2.12.2.jar:?]
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) ~[?:1.8.0_131]
	at io.luna.game.plugin.PluginBootstrap.initPlugins(PluginBootstrap.java:220) ~[classes/:?]
	at io.luna.game.plugin.PluginBootstrap.init(PluginBootstrap.java:170) ~[classes/:?]
	at io.luna.game.plugin.PluginBootstrap.call(PluginBootstrap.java:132) ~[classes/:?]
	at io.luna.game.plugin.PluginBootstrap.call(PluginBootstrap.java:40) ~[classes/:?]
	at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:111) ~[guava-22.0.jar:?]
	at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:58) ~[guava-22.0.jar:?]
	at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:75) ~[guava-22.0.jar:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_131]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_131]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]

In order to begin the standardization of plugin errors, a few prerequisites must be met

  • Remove ScalaConsole from PluginBootstrap (No longer needed in Scala 2.12)
  • Scrap PluginFailureException and ScriptException
  • EventListeners store the plugin file name as metadata

And the new standard should look something like

ERROR: FAILED: 'my_plugin.scala' 
  CAUSED BY: unbound placeholder parameter
  @ LINE 50: case _ => _

Plugin dependency orders should be package specific

Instead of there being one `dependencies.toml`` file to handle plugin evaluation order, one should be able to have one per directory. This would allow for less confusion— only having dependency orders for specific pieces of content.

For example, say one has three related scripts in the same folder: items.scala, give_items.scala, take_items.scala

And lets say both give_items.scala and take_items.scala depend on data within items.scala. You could then define dependencies.toml within the folder to ensure items.scala is interpreted first.

[dependencies]
names = [
  "items.scala"
]

Some big changes to would have to be made to plugins and the bootstraps in order to nicely support this

  • The global dependencies.toml should be scrapped
  • Plugins would have to be evaluated directory-by-directory
  • All current plugins should be in their own folder regardless of related plugin count or if they have a dependency file, for consistency

Optimizations to argument-based event interception

For code that has a lot of matching, ie.

on[SomeEvent](1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  11, 12, 13, 14, 15, 16, 17, 18, 19, 20) { msg =>
  ...
}

It should be translated to something like

val setMatch$one = Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 
  15, 16, 17, 18, 19, 20)
on[SomeEvent] { msg =>
  if(setMatch$one.contains(msg.getId)) {
    ...
    msg.terminate
  }
}

By bootstrap.scala

Is it worth the time to do this? Is it even possible? Until the following questions are answered, this issue will remain experimental

Does not build

I downloaded this repo and I can't compile it

It seems there are files missing

For example

Server.java
import io.luna.game.model.item.Shop;

But there is no such file

Same for
Bank.java
import io.luna.game.model.inter.StaticInventoryInterface;

Entity prioritization in regions

Before implementing this completely I need be sure of it's implications on the current design of the region system and performance.

I might just add a flag to "luna.toml" that lets the user choose if they want to turn it on or off (smaller servers can leave it off, while larger servers might require it).

Stackoverflow

I tried running the server and got this

08 Feb 2017 23:58:27 io.luna.game.plugin.PluginBootstrap [LunaInitializationThread] ERROR: Catching java.lang.StackOverflowError: null at scala.tools.nsc.typechecker.Contexts$Context.bufferErrors(Contexts.scala:332) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Contexts$Context.reportErrors(Contexts.scala:333) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.silent(Typers.scala:672) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.normalTypedApply$1(Typers.scala:4524) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4580) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5343) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5360) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.transform.Erasure$Eraser.typed1(Erasure.scala:698) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5396) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5423) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5370) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5374) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5472) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5480) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.transform.Erasure$Eraser.adaptMember(Erasure.scala:644) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.transform.Erasure$Eraser.typed1(Erasure.scala:698) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5396) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5423) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5370) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5374) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$99.apply(Typers.scala:4525) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$99.apply(Typers.scala:4525) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.silent(Typers.scala:680) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.normalTypedApply$1(Typers.scala:4524) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4580) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5343) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5360) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.transform.Erasure$Eraser.typed1(Erasure.scala:698) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5396) ~[scala-compiler-2.11.8.jar:?] at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5423) ~[scala-compiler-2.11.8.jar:?]

Movement is a bit janky

Especially running, which just randomly stops even though the run button is on. Also does this weird thing where it steps back a few of your previous steps

I also need to confirm that the run energy algorithm works correctly (which I can't do until running is fixed)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.