-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.bloop/ | ||
.metals/ | ||
.vscode/ | ||
out/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
rules = [ | ||
DisableSyntax, | ||
OrganizeImports | ||
] | ||
|
||
DisableSyntax { | ||
noVars = true, | ||
noThrows = true, | ||
# noNulls = true, | ||
# noWhileLoops = true, | ||
noReturns = true, | ||
noAsInstanceOf = true, | ||
noIsInstanceOf = true, | ||
noDefaultArgs = true, | ||
noFinalVal = true, | ||
noFinalize = true, | ||
noValPatterns = true, | ||
} | ||
|
||
OrganizeImports { | ||
removeUnused = false, | ||
coalesceToWildcardImportThreshold = 4, | ||
expandRelative = true, | ||
groupedImports = AggressiveMerge, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
version = "3.3.1" | ||
runner.dialect = scala3 | ||
align.preset = more | ||
|
||
maxColumn = 100 | ||
newlines.afterCurlyLambdaParams = squash | ||
rewrite.rules = [ | ||
SortImports, | ||
RedundantBraces, | ||
SortModifiers, | ||
PreferCurlyFors | ||
] | ||
rewrite.redundantBraces.stringInterpolation = true | ||
rewrite.scala3.removeOptionalBraces = true | ||
|
||
fileOverride { | ||
"glob:**/build.sc" { | ||
runner.dialect = scala213 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import $ivy.`com.goyeau::mill-scalafix:0.2.6` | ||
|
||
import mill.scalalib._ | ||
import mill.scalalib.scalafmt._ | ||
import com.goyeau.mill.scalafix.ScalafixModule | ||
|
||
object luafu extends ScalaModule with ScalafmtModule with ScalafixModule { | ||
def scalaVersion = "3.1.0" | ||
def scalacOptions = super.scalacOptions() ++ customScalaOpts | ||
|
||
def ivyDeps = Agg( | ||
ivy"org.ow2.asm:asm:9.2", | ||
ivy"org.ow2.asm:asm-commons:9.2" | ||
) | ||
|
||
def scalafixIvyDeps = super.scalafixIvyDeps() ++ Agg( | ||
ivy"com.github.liancheng::organize-imports:0.6.0" | ||
) | ||
|
||
def customScalaOpts = | ||
Seq( | ||
"-feature", | ||
"-unchecked", | ||
"-deprecation", | ||
"-Wunused:all", | ||
"-Xfatal-warnings" | ||
) | ||
|
||
def check = T { | ||
T.sequence(Seq(fix("--check"), checkFormat())) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import org.objectweb.asm._ | ||
import org.objectweb.asm.tree.{ClassNode, MethodNode} | ||
|
||
import java.lang.reflect.{Method, Modifier} | ||
import java.nio.file.{Files, Paths} | ||
import java.util.zip.{ZipEntry, ZipFile} | ||
import scala.collection.mutable.ListBuffer | ||
import scala.io.{Source, StdIn} | ||
import scala.jdk.CollectionConverters._ | ||
import scala.util.control.Exception.{allCatch, ultimately} | ||
|
||
object Luafu: | ||
def main(args: Array[String]): Unit = | ||
val jar = Jar(ZipFile("lib\\wakfu-client.jar")) | ||
val descriptors = ScriptDescriptors.resolve(jar).get | ||
val context = ScriptContext.load(jar, descriptors).get | ||
|
||
getClass.getClassLoader | ||
.loadClass("com.ankamagames.wakfu.client.WakfuClient") | ||
.getMethod("main", classOf[Array[String]]) | ||
.invoke(null, Array[String]()) | ||
|
||
while (true) { | ||
val line = StdIn.readLine | ||
allCatch.withApply(println)(context.interpret(line)) | ||
} | ||
|
||
case class ScriptDescriptors( | ||
scriptManagerClass: String, | ||
loadScriptDescriptor: String | ||
): | ||
val scriptLibraryClass: String = | ||
Type.getArgumentTypes(loadScriptDescriptor)(1).getElementType.getClassName | ||
|
||
val scriptClass: String = | ||
Type.getReturnType(loadScriptDescriptor).getClassName | ||
|
||
object ScriptDescriptors: | ||
def resolve(jar: Jar): Option[ScriptDescriptors] = | ||
for | ||
scriptManagerClass <- jar.classes | ||
.map { clazz => | ||
val finder = FieldConstantFinder(str => str == ".lua" || str == "script") | ||
clazz.accept(finder, Opcodes.ASM9) | ||
|
||
if (finder.values.length == 2) | ||
val node = ClassNode() | ||
clazz.accept(node, Opcodes.ASM9) | ||
Some(node) | ||
else None | ||
} | ||
.collectFirst { case Some(res) => res } | ||
|
||
loadScriptMethod <- | ||
scriptManagerClass.methods.asScala.find { m => | ||
val args = Type.getArgumentTypes(m.desc) | ||
val ret = Type.getReturnType(m.desc) | ||
|
||
(m.access & Opcodes.ACC_PRIVATE) != 0 && | ||
(args.length == 3) && | ||
ret.getSort == Type.OBJECT && | ||
args(0).getSort == Type.OBJECT && | ||
args(1).getSort == Type.ARRAY && | ||
args(2) == Type.BOOLEAN_TYPE | ||
} | ||
yield ScriptDescriptors(scriptManagerClass.name, loadScriptMethod.desc) | ||
|
||
class ScriptContext( | ||
scriptManager: AnyRef, | ||
scriptLibraries: AnyRef, | ||
loadScript: Method, | ||
runScript: Method | ||
): | ||
loadScript.setAccessible(true) | ||
private val pattern = """dofile\(['"](.*)['"]\)""".r | ||
|
||
def interpret(code: String): Unit = | ||
val source = pattern.findFirstMatchIn(code) match | ||
case Some(res) => | ||
val file = res.group(1) | ||
val path = Paths.get("luafu").resolve("scripts").resolve(file) | ||
new String(Files.readAllBytes(path)) | ||
case None => code | ||
|
||
val script = loadScript.invoke(scriptManager, source, scriptLibraries, false) | ||
runScript.invoke(script, java.util.HashMap()) | ||
|
||
object ScriptContext: | ||
def load(jar: Jar, descriptors: ScriptDescriptors): Option[ScriptContext] = | ||
val managerClass = getClass.getClassLoader.loadClass(descriptors.scriptManagerClass) | ||
val scriptClass = getClass.getClassLoader.loadClass(descriptors.scriptClass) | ||
|
||
for | ||
scriptManager <- managerClass.getDeclaredMethods | ||
.find(m => Modifier.isStatic(m.getModifiers) && m.getParameterCount == 0) | ||
.map(_.invoke(null)) | ||
scriptLibraries = loadAllLibraries(jar, descriptors) | ||
|
||
loadScript <- managerClass.getDeclaredMethods | ||
.find(m => Type.getMethodDescriptor(m) == descriptors.loadScriptDescriptor) | ||
runScript <- scriptClass.getDeclaredMethods | ||
.find(m => m.getParameterTypes.sameElements(Array(classOf[java.util.Map[_, _]]))) | ||
yield ScriptContext(scriptManager, scriptLibraries, loadScript, runScript) | ||
|
||
def loadAllLibraries(jar: Jar, descriptors: ScriptDescriptors): AnyRef = | ||
val libraryBaseClass = getClass.getClassLoader.loadClass(descriptors.scriptLibraryClass) | ||
val libraryClasses = jar.classes.filter(_.getSuperName == descriptors.scriptLibraryClass) | ||
val array = java.lang.reflect.Array.newInstance(libraryBaseClass, libraryClasses.length) | ||
|
||
for ((libClassReader, i) <- libraryClasses.zipWithIndex) | ||
getClass.getClassLoader | ||
.loadClass(libClassReader.getClassName) | ||
.getDeclaredMethods | ||
.find(m => Modifier.isStatic(m.getModifiers) && m.getParameterCount == 0) | ||
.foreach(m => java.lang.reflect.Array.set(array, i, m.invoke(null))) | ||
|
||
array | ||
|
||
class Jar(zip: ZipFile): | ||
def classes: Iterator[ClassReader] = | ||
zip.entries.asScala | ||
.filter((entry: ZipEntry) => entry.getName.endsWith(".class")) | ||
.map { (entry: ZipEntry) => | ||
val is = zip.getInputStream(entry) | ||
ultimately(is.close())(ClassReader(is)) | ||
} | ||
|
||
class FieldConstantFinder(fieldPred: AnyRef => Boolean) extends ClassVisitor(Opcodes.ASM9): | ||
val values = ListBuffer.empty[AnyRef] | ||
|
||
override def visitField( | ||
access: Int, | ||
name: String, | ||
descriptor: String, | ||
signature: String, | ||
value: AnyRef | ||
): FieldVisitor = | ||
if (fieldPred(value)) | ||
this.values += value | ||
super.visitField(access, name, descriptor, signature, value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
cd .. | ||
$baseCp = (Get-ChildItem -Path "lib" | %{ $_.FullName }) -join ";" | ||
$addedCp = "{0}\luafu\luafu.jar;{1}" -f (pwd),$baseCp | ||
|
||
jre\win32\x64\bin\java.exe "-Djava.library.path=natives\win32\x64" -cp $addedCp Luafu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
local playerId = Context.getPlayer() | ||
local x, y, z = Mobile.getMobilePosition(playerId) | ||
|
||
Mobile.setMobileAnimation(playerId , "AnimEmote-Presenter") | ||
-- Mobile.setMobileVisible(Context.getPlayer(), false) | ||
|
||
Camera.setZoomFactor(2.5) | ||
-- Camera.setUserZoomLocked(true) | ||
|
||
-- UI.setUIVisible(false) | ||
UI.loadTutorialDialog("bossSmasher","event.boss.smasher.13.title","event.boss.smasher.13.description") | ||
-- UI.displayBackground(53) | ||
|
||
-- display dialogs | ||
-- invoke(0, 1, "UI.displaySplashText","quest.astrub.nation.choix") | ||
invoke(0, 1, "UI.loadTutorialDialog", "beta.shushu", "beta.title", "beta.09.11.2021") | ||
|
||
BubbleText.showText(playerId, "testing", 0, 40, false) | ||
|
||
Sound.playLocalSound(11, 420800522001, 40, 2, -1, 0) | ||
|
||
-- adds a compass location | ||
UI.addCompass("compass", x, y + 1, z) | ||
|
||
-- spawns a puddly | ||
Actor.createActor(Mobile.generateClientMobileId(), 122601633, x, y + 1, z, 4) |