From 280f9f24996a7fd99a2f0d0a67610be6d4467ec4 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Mon, 11 Dec 2017 21:42:31 -0800 Subject: [PATCH] Add outputting info to file and info about ticks gained - Now requires luafilesystem (lfs) - Fixed negative number display in friendlyNumber/friendlyTime --- .gitignore | 84 ++++++++++++++++++++++++++++++++++++++ README.md | 15 +++++-- d2info.lua | 39 +++++++++--------- d2info/constants.lua | 6 +++ d2info/output.lua | 51 +++++++++++++++++++++++ d2info/session.lua | 18 ++++++-- d2info/utils.lua | 38 +++++++++++++---- scripts/build-luastatic.sh | 22 ++++++++-- 8 files changed, 236 insertions(+), 37 deletions(-) create mode 100644 .gitignore create mode 100644 d2info/output.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f470536 --- /dev/null +++ b/.gitignore @@ -0,0 +1,84 @@ +output + +# Created by https://www.gitignore.io/api/lua,linux,windows + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Lua ### +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/lua,linux,windows \ No newline at end of file diff --git a/README.md b/README.md index 24993d6..444dca2 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,14 @@ Simply grab the [latest .exe build from the releases page](https://github.com/sq ### Running using Lua -- Build [memreader](https://github.com/squeek502/memreader) and [sleep](https://github.com/squeek502/sleep) and make the resulting .dll's available to Lua's `package.cpath`. +- Build [memreader](https://github.com/squeek502/memreader), [sleep](https://github.com/squeek502/sleep), and [luafilesystem](https://github.com/keplerproject/luafilesystem) and make the resulting .dll's available to Lua's `package.cpath`. - Run `lua d2info.lua` -## Sample Output +## Output -Currently, the information is just output to the console window. Soon, data will be (optionally?) put into files so that it can easily be added to stream overlays. +The information is output to both the console window and to individual text files (in the directory `output`, relative to d2info) to allow the info to be easily added to stream overlays. +Console output example: ``` CoolGuy @@ -41,6 +42,14 @@ Est time until level 96: 11h (using game-time xp/min) 23h (using current game's xp/min) 8h22m (using last game's xp/min) + +Exp gained (overall): 2.4m +Exp gained (current game): 179.8k +Exp gained (last game): 156.1k + +Ticks gained (overall): 1.4 +Ticks gained (current game): 0.1 +Ticks gained (last game): 0.1 ``` ## Acknowledgements diff --git a/d2info.lua b/d2info.lua index 9a463ee..3d63070 100644 --- a/d2info.lua +++ b/d2info.lua @@ -1,17 +1,17 @@ local memreader = require('memreader') local D2Reader = require('d2info.d2reader') local sleep = require('sleep') -local friendlyNumber = require('d2info.utils').friendlyNumber -local friendlyTime = require('d2info.utils').friendlyTime local Session = require('d2info.session') +local Output = require('d2info.output') memreader.debugprivilege(true) local reader = D2Reader.new() local sessions = {} -local lastPlayer = nil +local output = Output.new() +local lastInfo = {} +local UPDATE_PERIOD = 1000 while true do - os.execute('cls') local player = reader:getPlayerName() if player then local exp, lvl = reader:getExperience() @@ -20,7 +20,7 @@ while true do sessions[player] = {} sessions[player].total = Session.new(exp, lvl) end - if player ~= lastPlayer then + if sessions[player].current == nil then sessions[player].current = Session.new(exp, lvl) end @@ -28,30 +28,29 @@ while true do current:update(exp, lvl) total:update(exp, lvl) - print(string.format("%s\n", player)) - print(string.format("Overall (real-time): %s xp/min", friendlyNumber(total:realTimeExpPerMin()))) - print(string.format("Overall (game-time): %s xp/min", friendlyNumber(total:durationExpPerMin()))) - print(string.format("Current game: %s xp/min", friendlyNumber(current:realTimeExpPerMin()))) - print(string.format("Last game: %s xp/min", last and friendlyNumber(last:durationExpPerMin()) or "?")) - print(string.format("\nEst time until level %d:", lvl+1)) - print(string.format(" %s (using real-time xp/min)", friendlyTime(total:realTimeToNextLevel()))) - print(string.format(" %s (using game-time xp/min)", friendlyTime(total:gameTimeToNextLevel()))) - print(string.format(" %s (using current game's xp/min)", friendlyTime(current:gameTimeToNextLevel()))) - print(string.format(" %s (using last game's xp/min)", last and friendlyTime(last:gameTimeToNextLevel()) or "?")) + output:toScreen(player, lvl, total, current, last) + output:toFile(player, lvl, total, current, last) current:incrementDuration() total:incrementDuration() - lastPlayer = player + lastInfo.player = player + lastInfo.level = lvl elseif reader.status ~= nil then + os.execute('cls') print(reader.status) else + os.execute('cls') print("No player") - if lastPlayer ~= nil then - sessions[lastPlayer].last = sessions[lastPlayer].current - lastPlayer = nil + if lastInfo.player ~= nil and sessions[lastInfo.player].current ~= nil then + sessions[lastInfo.player].last = sessions[lastInfo.player].current + sessions[lastInfo.player].current = nil end + + -- need to update files here because otherwise they wouldn't update + -- while at the menu screen + output:toFile(lastInfo.player, lastInfo.level, sessions[lastInfo.player].total, sessions[lastInfo.player].current, sessions[lastInfo.player].last) end - sleep(1000) + sleep(UPDATE_PERIOD) end diff --git a/d2info/constants.lua b/d2info/constants.lua index 9f72133..c79403c 100644 --- a/d2info/constants.lua +++ b/d2info/constants.lua @@ -21,6 +21,12 @@ return { exe = "Game.exe", windowTitle = "Diablo II", + gui = { + expBar = { + ticks=120, + } + }, + stats = { strength = 0, energy = 1, diff --git a/d2info/output.lua b/d2info/output.lua new file mode 100644 index 0000000..0cbd1bf --- /dev/null +++ b/d2info/output.lua @@ -0,0 +1,51 @@ +local utils = require('d2info.utils') +local lfs = require('lfs') +local printf, friendlyNumber, friendlyTime, toFile = utils.printf, utils.friendlyNumber, utils.friendlyTime, utils.toFile + +local Output = {} +Output.__index = Output + +function Output.new(outputDir) + local self = setmetatable({}, Output) + self.outputDir = outputDir or "output" + if not lfs.attributes(self.outputDir) then + assert(lfs.mkdir(self.outputDir)) + end + return self +end + +function Output:toScreen(player, level, total, current, last) + os.execute('cls') + printf("%s\n", player) + printf("Overall (real-time): %s xp/min", friendlyNumber(total:realTimeExpPerMin())) + printf("Overall (game-time): %s xp/min", friendlyNumber(total:durationExpPerMin())) + printf("Current game: %s xp/min", current and friendlyNumber(current:realTimeExpPerMin()) or "-") + printf("Last game: %s xp/min", last and friendlyNumber(last:durationExpPerMin()) or "-") + printf("\nEst time until level %d:", level+1) + printf(" %s (using real-time xp/min)", friendlyTime(total:realTimeToNextLevel())) + printf(" %s (using game-time xp/min)", friendlyTime(total:gameTimeToNextLevel())) + printf(" %s (using current game's xp/min)", current and friendlyTime(current:gameTimeToNextLevel()) or "-") + printf(" %s (using last game's xp/min)", last and friendlyTime(last:gameTimeToNextLevel()) or "-") + printf("\nExp gained (overall): %s", friendlyNumber(total:expGained())) + printf("Exp gained (current game): %s", current and friendlyNumber(current:expGained()) or "-") + printf("Exp gained (last game): %s", last and friendlyNumber(last:expGained()) or "-") + printf("\nTicks gained (overall): %0.1f", total:ticksGained()) + printf("Ticks gained (current game): %0.1f", current and current:ticksGained() or 0) + printf("Ticks gained (last game): %0.1f", last and last:ticksGained() or 0) +end + +function Output:toFile(player, level, total, current, last) + toFile(self.outputDir .. "/xpmin-realtime.txt", friendlyNumber(total:realTimeExpPerMin())) + toFile(self.outputDir .. "/xpmin-gametime.txt", friendlyNumber(total:durationExpPerMin())) + toFile(self.outputDir .. "/xpmin-currentgame.txt", current and friendlyNumber(current:realTimeExpPerMin()) or "-") + toFile(self.outputDir .. "/xpmin-lastgame.txt", last and friendlyNumber(last:durationExpPerMin()) or "-") + toFile(self.outputDir .. "/timetolevel-realtime.txt", friendlyTime(total:realTimeToNextLevel())) + toFile(self.outputDir .. "/timetolevel-gametime.txt", friendlyTime(total:gameTimeToNextLevel())) + toFile(self.outputDir .. "/timetolevel-currentgame.txt", current and friendlyTime(current:gameTimeToNextLevel()) or "-") + toFile(self.outputDir .. "/timetolevel-lastgame.txt", last and friendlyTime(last:gameTimeToNextLevel()) or "-") + toFile(self.outputDir .. "/ticksgained-overall.txt", string.format("%0.1f", total:ticksGained())) + toFile(self.outputDir .. "/ticksgained-currentgame.txt", current and string.format("%0.1f", current:ticksGained()) or "-") + toFile(self.outputDir .. "/ticksgained-lastgame.txt", last and string.format("%0.1f", last:ticksGained()) or "-") +end + +return Output diff --git a/d2info/session.lua b/d2info/session.lua index 7f97824..8a2eec7 100644 --- a/d2info/session.lua +++ b/d2info/session.lua @@ -1,4 +1,5 @@ local constants = require('d2info.constants') +local utils = require('d2info.utils') local Session = {} Session.__index = Session @@ -32,6 +33,15 @@ function Session:incrementDuration(inc) self.duration = self.duration + inc end +function Session:expGained() + return self.exp - self.startExp +end + +function Session:ticksGained() + local gainedIntoLevel = constants.experience[self.level] + self:expGained() + return utils.expToTicks(gainedIntoLevel, self.level) +end + local function expPerMin(expGained, duration) local mins = duration / 60 if mins == 0 then return 0 end @@ -39,11 +49,11 @@ local function expPerMin(expGained, duration) end function Session:realTimeExpPerMin() - return expPerMin(self.exp - self.startExp, os.time() - self.startTime) + return expPerMin(self:expGained(), os.time() - self.startTime) end function Session:durationExpPerMin() - return expPerMin(self.exp - self.startExp, self.duration) + return expPerMin(self:expGained(), self.duration) end local function secondsToNextLevel(exp, level, expGained, duration) @@ -55,11 +65,11 @@ local function secondsToNextLevel(exp, level, expGained, duration) end function Session:realTimeToNextLevel() - return secondsToNextLevel(self.exp, self.level, self.exp - self.startExp, os.time() - self.startTime) + return secondsToNextLevel(self.exp, self.level, self:expGained(), os.time() - self.startTime) end function Session:gameTimeToNextLevel() - return secondsToNextLevel(self.exp, self.level, self.exp - self.startExp, self.duration) + return secondsToNextLevel(self.exp, self.level, self:expGained(), self.duration) end return Session diff --git a/d2info/utils.lua b/d2info/utils.lua index 6a47826..3923a29 100644 --- a/d2info/utils.lua +++ b/d2info/utils.lua @@ -1,3 +1,4 @@ +local constants = require('d2info.constants') local utils = {} function utils.friendlyVersion(ver) @@ -17,21 +18,24 @@ function utils.tableMerge(a, b) end function utils.friendlyNumber(num) - if num > 1000000 then - return string.format("%0.2fm", num / 1000000) - elseif num > 1000 then + local abs = math.abs(num) + if abs >= 1000000000 then + return string.format("%0.1fb", num / 1000000000) + elseif abs >= 1000000 then + return string.format("%0.1fm", num / 1000000) + elseif abs >= 1000 then return string.format("%0.1fk", num / 1000) end - return string.format("%u", num) + return string.format("%d", num) end local secondsPerMin = 60 local secondsPerHour = secondsPerMin * 60 local secondsPerDay = secondsPerHour * 24 -function utils.friendlyTime(seconds, days) - if seconds == nil then +function utils.friendlyTime(seconds, showDays) + if seconds == nil or seconds < 0 then return "-" - elseif days and seconds >= secondsPerDay then + elseif showDays and seconds >= secondsPerDay then return string.format("%ud%02uh", math.floor(seconds / secondsPerDay), (seconds % secondsPerDay) / secondsPerHour) elseif seconds >= secondsPerHour*10 then return string.format("%uh", math.floor(seconds / secondsPerHour)) @@ -44,4 +48,24 @@ function utils.friendlyTime(seconds, days) end end +function utils.printf(...) + print(string.format(...)) +end + +function utils.toFile(filename, txt) + local f = assert(io.open(filename, "w")) + f:write(txt) + f:close() +end + +-- Converts exp to GUI 'ticks' of the experience bar +function utils.expToTicks(exp, level) + if level == 99 then return 0 end + local maxTicks = constants.gui.expBar.ticks + local expRange = constants.experience[level+1] - constants.experience[level] + local expGotten = exp - constants.experience[level] + local percentLeveled = expGotten / expRange + return percentLeveled * maxTicks +end + return utils diff --git a/scripts/build-luastatic.sh b/scripts/build-luastatic.sh index 853fce8..60fd308 100755 --- a/scripts/build-luastatic.sh +++ b/scripts/build-luastatic.sh @@ -14,9 +14,11 @@ mkdir build cd build LUA_VERSION=5.1.5 +LFS_VERSION=1.7.0-2 -# ensure luastatic is available -which luastatic +# ensure luastatic and luarocks are available +which luastatic || { echo "luastatic not found"; exit 1; } +which luarocks || { echo "luarocks not found"; exit 1; } echo echo "=== Downloading Lua $LUA_VERSION ===" @@ -33,6 +35,11 @@ echo "=== Downloading sleep ===" echo git clone https://github.com/squeek502/sleep.git +echo +echo "=== Downloading LuaFileSystem $LFS_VERSION ===" +echo +luarocks unpack luafilesystem $LFS_VERSION + echo echo "=== Building Lua ===" echo @@ -59,6 +66,15 @@ make cp libsleep.a $BUILD_DIR cd $BUILD_DIR +echo +echo "=== Building LuaFileSystem $LFS_VERSION ===" +echo +cd luafilesystem-$LFS_VERSION/luafilesystem +x86_64-w64-mingw32-gcc -c -O2 src/lfs.c -I$BUILD_DIR/lua-$LUA_VERSION/src -o src/lfs.o +x86_64-w64-mingw32-ar rcs src/lfs.a src/lfs.o +cp src/lfs.a $BUILD_DIR +cd $BUILD_DIR + echo echo "=== Copying d2info sources ===" echo @@ -68,7 +84,7 @@ cp $ROOT_DIR/d2info.lua $BUILD_DIR echo echo "=== Building d2info.exe ===" echo -CC=x86_64-w64-mingw32-gcc luastatic d2info.lua d2info/*.lua liblua.a libmemreader.a libsleep.a /usr/x86_64-w64-mingw32/lib/libversion.a /usr/x86_64-w64-mingw32/lib/libpsapi.a -Ilua-$LUA_VERSION/src +CC=x86_64-w64-mingw32-gcc luastatic d2info.lua d2info/*.lua liblua.a libmemreader.a libsleep.a lfs.a /usr/x86_64-w64-mingw32/lib/libversion.a /usr/x86_64-w64-mingw32/lib/libpsapi.a -Ilua-$LUA_VERSION/src strip d2info.exe cd $ROOT_DIR