close #5 #6 simpler usage & SSL support

This commit is contained in:
Paul Liverman III 2018-05-25 16:32:09 -07:00
parent 55327f6fd2
commit c38ace983a
6 changed files with 315 additions and 208 deletions

View File

@ -5,30 +5,36 @@ published on [itch.io](https://itch.io/).
## Installation ## Installation
Just copy `check.lua` to where you want in your source. Copy `itchy.lua` to where you want in your source. It is recommended that you
install [luajit-request](https://github.com/LPGhatguy/luajit-request) as it
allows for itchy to use HTTPS connections.
## Usage ## Usage
Start it as a thread with a configuration table. Wait for "itchy" channel to Start it as a thread with a configuration table. Wait for "itchy" channel to
respond with a table of version information. respond with a table of version information.
```lua Require itchy, and run `check_version` with a configuration table. Periodically
versionChecker = love.thread.newThread("lib/itchy/check.lua") -- wherever you save it.. run `new_version` to see if data has been returned yet.
versionChecker:start({
target = "guard13007/asteroid-dodge", -- target/url must be defined
version = "1.0.0" -- optional, config options listed below
})
newVersion = love.thread.getChannel("itchy") ```lua
if newVersion:getCount() > 0 then local itchy = require "lib.itchy" -- or wherever you saved it
local data = newVersion:demand() -- see example data below local game = {
-- easiest usage is to just print something like this to the user target = "guard13007/asteroid-dodge", -- target or url must be defined
print("Version: 1.0.0 Latest version: " .. data.message) version = "1.0.0" -- optional, config options listed below
}
itchy:check_version(game)
-- somewhere where this will be called periodically
local data = itchy:new_version(game) -- passing the game table is not necessary
if data then
-- easiest usage, just print the message to the user
love.graphics.print("Version: 1.0.0 Latest: " .. data.message)
end end
``` ```
Since it is run as a thread, you can cancel it with `versionChecker:kill()` and You can cancel it with `itchy:kill_version_checker(game)`, and start a new
start it again or with a different configuration later with `:start()`. version checker with `itchy:check_version({})` any time you like.
### Version Information Example ### Version Information Example
@ -52,7 +58,7 @@ based on this value. If it is unable to extract it or compare, `version` and
`latest` will be `nil`. `latest` will be `nil`.
If HTTP status 200 (OK) is encountered or a version is successfully parsed from If HTTP status 200 (OK) is encountered or a version is successfully parsed from
a response, the script terminates (or moves on to checking on an `interval`.) a response, the script terminates (or moves on to checking on an interval.)
Otherwise, it will keep trying with an exponential back-off starting at a 1 Otherwise, it will keep trying with an exponential back-off starting at a 1
second delay, capped at retrying every 10 minutes. second delay, capped at retrying every 10 minutes.
@ -63,17 +69,25 @@ At minimum a `url` or `target` must be specified.
* `url` (string) If you have a different URL to check for the latest version * `url` (string) If you have a different URL to check for the latest version
from, you can specify it here. from, you can specify it here.
* `target` (string) The target string of your game on itch.io * `target` (string) The target string of your game on itch.io
(username/game-slug) (username/game-slug).
* `channel` (string) If you do not specify the channel name to look for on * `channel` (string) If you do not specify the channel name to look for on
itch.io, it will use `osx` for Mac OS / OS X, `win32` for Windows, `linux` for itch.io, it will use `osx` for Mac OS / OS X, `win32` for Windows, `linux` for
Linux, `android` for Android, `ios` for iOS, and if any other OS is returned Linux, `android` for Android, `ios` for iOS, and if any other OS is returned
by `love.system.getOS()` it will use that string as-is. by `love.system.getOS()` it will use that string as-is.
* `version` (string/number) Version of the game running right now. * `version` (string/number) Version of the game running right now.
* `proxy` (string) This library uses an [HTTP proxy](https://github.com/Guard13007/insecure-proxy)
for the HTTPS call to itch.io's API. By default it uses `https://104.236.139.220:16343`
which is on a DigitalOcean VPS I own. If you'd rather use a different proxy
server, you can specify it here.
* `interval` (number) If specified, a check for the latest version will happen * `interval` (number) If specified, a check for the latest version will happen
again every `interval` seconds. again every `interval` seconds.
* `thread_channel` (string) If specified, will use a different named thread * `luajit_request` (string) luajit-request is checked for in `.` and `lib/.`, if
channel to return results to. you have it elsewhere, specify its location here.
The following options are available, but generally should be left for itchy to
handle itself:
* `proxy` (string) An [HTTP proxy](https://github.com/Guard13007/insecure-proxy)
is used if luajit-request is unavailable, unless `proxy == false`. By default,
`https://104.236.139.220:16343` is used, which is running on a DigitalOcean
VPS I own. You can specify a different proxy here.
* `thread_channel` (string) itchy uses a channel named `itchy` for version
checking. You can call itchy's functions with different data tables and it
will use different threads & channels for each. You can also specify a channel
name here.

View File

@ -1,97 +0,0 @@
require("love.timer")
local thread, timer
do
local _obj_0 = love
thread, timer = _obj_0.thread, _obj_0.timer
end
local http = require("socket.http")
local check
check = function(data)
local send = thread.getChannel(data.thread_channel or "itchy")
local exponential_backoff = 1
while true do
local _continue_0 = false
repeat
do
local result = { }
if data.url then
result.body, result.status = http.request(data.url)
elseif data.proxy then
if not (data.target) then
result.message = "'target' or 'url' must be defined!"
send:push(result)
return false
end
result.body, result.status = http.request(tostring(data.proxy) .. "/get/https://api.itch.io/wharf/latest?target=" .. tostring(data.target) .. "&channel_name=" .. tostring(data.channel))
end
if not (result.body) then
result.message = "socket.http.request error: " .. tostring(result.status)
send:push(result)
return false
end
result.version = result.body:match('%s*{%s*"latest"%s*:%s*"(.+)"%s*}%s*')
result.version = tonumber(result.version) or result.version
if data.version then
result.latest = result.version == data.version
end
if result.status ~= 200 and (not result.version) then
result.message = "unknown, error getting latest version: HTTP " .. tostring(result.status) .. ", trying again in " .. tostring(exponential_backoff) .. " seconds"
send:push(result)
timer.sleep(exponential_backoff)
exponential_backoff = exponential_backoff * 2
if exponential_backoff > 10 * 60 then
exponential_backoff = 10 * 60
end
_continue_0 = true
break
elseif result.latest ~= nil then
if result.latest then
result.message = tostring(result.version) .. ", you have the latest version"
else
result.message = tostring(result.version) .. ", there is a newer version available!"
end
else
result.message = result.version
end
send:push(result)
return true
end
_continue_0 = true
until true
if not _continue_0 then
break
end
end
end
local start
start = function(data)
if not (data.proxy or data.url) then
data.proxy = "http://45.55.113.149:16343"
end
if not (data.channel) then
require("love.system")
local os = love.system.getOS()
local _exp_0 = os
if "OS X" == _exp_0 then
data.channel = "osx"
elseif "Windows" == _exp_0 then
data.channel = "win32"
elseif "Linux" == _exp_0 then
data.channel = "linux"
elseif "Android" == _exp_0 then
data.channel = "android"
elseif "iOS" == _exp_0 then
data.channel = "ios"
else
data.channel = os
end
end
check(data)
if data.interval then
while true do
timer.sleep(data.interval)
check(data)
end
end
end
return start(...)

View File

@ -1,79 +0,0 @@
require "love.timer"
import thread, timer from love
http = require "socket.http"
check = (data) ->
send = thread.getChannel data.thread_channel or "itchy"
exponential_backoff = 1
while true
result = {}
if data.url
result.body, result.status = http.request data.url
elseif data.proxy
unless data.target
result.message = "'target' or 'url' must be defined!"
send\push result
return false
result.body, result.status = http.request "#{data.proxy}/get/https://api.itch.io/wharf/latest?target=#{data.target}&channel_name=#{data.channel}"
unless result.body
result.message = "socket.http.request error: #{result.status}"
send\push result
return false
result.version = result.body\match '%s*{%s*"latest"%s*:%s*"(.+)"%s*}%s*'
result.version = tonumber(result.version) or result.version
result.latest = if data.version
result.version == data.version
if result.status != 200 and (not result.version)
result.message = "unknown, error getting latest version: HTTP #{result.status}, trying again in #{exponential_backoff} seconds"
send\push result
timer.sleep exponential_backoff
exponential_backoff *= 2
exponential_backoff = 10 * 60 if exponential_backoff > 10 * 60 -- maximum backoff is 10 minutes
continue
elseif result.latest != nil
if result.latest
result.message = "#{result.version}, you have the latest version"
else
result.message = "#{result.version}, there is a newer version available!"
else
result.message = result.version
send\push result
return true
-- data should be a table of information
start = (data) ->
data.proxy = "http://45.55.113.149:16343" unless data.proxy or data.url
-- channel can be autodetected if not specified
unless data.channel
require "love.system"
os = love.system.getOS!
switch os
when "OS X"
data.channel = "osx"
when "Windows"
data.channel = "win32"
when "Linux"
data.channel = "linux"
when "Android"
data.channel = "android"
when "iOS"
data.channel = "ios"
else
data.channel = os
check data
-- if we should check again every x seconds, wait, and do so
if data.interval
while true
timer.sleep data.interval
check data
start(...)

136
itchy.lua Normal file
View File

@ -0,0 +1,136 @@
local thread
thread = function(...)
require("love.thread")
require("love.timer")
local timer
do
local _obj_0 = love
thread, timer = _obj_0.thread, _obj_0.timer
end
local http = require("socket.http")
local _, libcurl = pcall(function()
return require("luajit-request")
end)
if not (libcurl) then
_, libcurl = pcall(function()
return require("lib.luajit-request")
end)
end
local request
request = function(data)
local result = { }
if not (libcurl) then
if data.luajit_request then
_, libcurl = pcall(function()
return require(data.luajit_request)
end)
end
end
if libcurl then
local response = libcurl.send(data.url or "https://api.itch.io/wharf/latest?target=" .. tostring(data.target) .. "&channel_name=" .. tostring(data.channel))
result.body = response.body
result.status = response.code
else
if not (data.proxy) then
result.message = "Could not load libcurl."
return nil, result
end
result.body, result.status = http.request(data.url or tostring(data.proxy) .. "/get/https://api.itch.io/wharf/latest?target=" .. tostring(data.target) .. "&channel_name=" .. tostring(data.channel))
if not (result.body) then
result.message = "socket.http.request error: " .. tostring(result.status)
result.status = nil
return nil, result
end
end
return true, result
end
local check
check = function(data)
local send = thread.getChannel(data.thread_channel or "itchy")
local exponential_backoff = 1
while true do
local _continue_0 = false
repeat
do
local result = { }
if not (data.url or data.target) then
result.message = "'target' or 'url' be must be defined!"
send:push(result)
return false
end
local ok
ok, result = request(data)
if not (ok) then
send:push(result)
return false
end
result.version = result.body:match('%s*{%s*"latest"%s*:%s*"(.+)"%s*}%s*')
result.version = tonumber(result.version) or result.version
if data.version then
result.latest = result.version == data.version
end
if result.status ~= 200 and (not result.version) then
result.message = "unknown, error getting latest version: HTTP " .. tostring(result.status) .. ", trying again in " .. tostring(exponential_backoff) .. " seconds..."
send:push(result)
timer.sleep(exponential_backoff)
exponential_backoff = exponential_backoff * 2
if exponential_backoff > 10 * 60 then
exponential_backoff = 10 * 60
end
_continue_0 = true
break
elseif result.latest ~= nil then
if result.latest then
result.message = tostring(result.version) .. ", you have the latest vesion"
else
result.message = tostring(result.version) .. ", there is a newer version available!"
end
else
result.message = result.version
end
send:push(result)
return true
end
_continue_0 = true
until true
if not _continue_0 then
break
end
end
end
local start
start = function(data)
if data.proxy == nil and (not data.url) then
data.proxy = "http://45.55.113.149:16343"
end
if not (data.channel) then
require("love.system")
local os = love.system.getOS()
local _exp_0 = os
if "OS X" == _exp_0 then
data.channel = "osx"
elseif "Windows" == _exp_0 then
data.channel = "win32"
elseif "Linux" == _exp_0 then
data.channel = "linux"
elseif "Android" == _exp_0 then
data.channel = "android"
elseif "iOS" == _exp_0 then
data.channel = "ios"
else
data.channel = os
end
end
check(data)
if data.interval then
while true do
timer.sleep(data.interval)
check(data)
end
end
end
return start(...)
end
if not (love.graphics or love.window) then
return thread(...)
end

132
itchy.moon Normal file
View File

@ -0,0 +1,132 @@
thread = (...) ->
require "love.thread"
require "love.timer"
import thread, timer from love
http = require "socket.http"
_, libcurl = pcall -> return require "luajit-request"
unless libcurl
_, libcurl = pcall -> return require "lib.luajit-request"
request = (data) ->
result = {}
unless libcurl
if data.luajit_request
_, libcurl = pcall -> return require data.luajit_request
if libcurl
response = libcurl.send data.url or "https://api.itch.io/wharf/latest?target=#{data.target}&channel_name=#{data.channel}"
result.body = response.body
result.status = response.code
else
unless data.proxy
result.message = "Could not load libcurl."
return nil, result
result.body, result.status = http.request data.url or "#{data.proxy}/get/https://api.itch.io/wharf/latest?target=#{data.target}&channel_name=#{data.channel}"
unless result.body
result.message = "socket.http.request error: #{result.status}"
result.status = nil
return nil, result
return true, result
check = (data) ->
send = thread.getChannel data.thread_channel or "itchy"
exponential_backoff = 1
while true
result = {}
unless data.url or data.target
result.message = "'target' or 'url' be must be defined!"
send\push result
return false
ok, result = request data
unless ok
send\push result
return false
result.version = result.body\match '%s*{%s*"latest"%s*:%s*"(.+)"%s*}%s*'
result.version = tonumber(result.version) or result.version
result.latest = if data.version
result.version == data.version
if result.status != 200 and (not result.version)
result.message = "unknown, error getting latest version: HTTP #{result.status}, trying again in #{exponential_backoff} seconds..."
send\push result
timer.sleep exponential_backoff
exponential_backoff *= 2
exponential_backoff = 10 * 60 if exponential_backoff > 10 * 60 -- maximum backoff is 10 minutes
continue
elseif result.latest != nil
if result.latest
result.message = "#{result.version}, you have the latest vesion"
else
result.message = "#{result.version}, there is a newer version available!"
else
result.message = result.version
send\push result
return true
-- data should be a table of information
start = (data) ->
data.proxy = "http://45.55.113.149:16343" if data.proxy == nil and (not data.url)
-- channel can be autodetected if not specified
unless data.channel
require "love.system"
os = love.system.getOS!
switch os
when "OS X"
data.channel = "osx"
when "Windows"
data.channel = "win32"
when "Linux"
data.channel = "linux"
when "Android"
data.channel = "android"
when "iOS"
data.channel = "ios"
else
data.channel = os
check data
-- if we should check again every x seconds, wait, and do so
if data.interval
while true
timer.sleep data.interval
check data
start(...)
-- detect if we are running in a thread, run directly if we are
return thread(...) unless love.graphics or love.window
thread_data = love.filesystem.newFileData string.dump(thread), "itchy version checker"
counter = 1
configs, results = {}, {}
local default_data
itchy = {
check_version: (data) ->
default_data = data unless default_data
if (not data.thread_channel) and next configs
data.thread_channel = "itchy-#{counter}"
counter += 1
configs[data] = data
love.thread.newThread(thread_data)\start data
new_version: (data=default_data) ->
if data and configs[data]
channel = love.thread.getChannel data.thread_channel or "itchy"
if channel\getCount! > 0
results[data] = channel\demand!
return results[data] -- nil or data (new or old)
kill_version_checker: (data=default_data) ->
configs[data] = nil
results[data] = nil
default_data = nil if data == default_data
-- we don't kill the thread, as that can crash the game
}
return itchy

View File

@ -1,12 +1,13 @@
versionChecker = love.thread.newThread("lib/itchy/check.lua") -- wherever you save it.. local itchy = require "lib.itchy" -- or wherever you saved it
versionChecker:start({ local game = {
target = "guard13007/asteroid-dodge", -- target/url must be defined target = "guard13007/asteroid-dodge", -- target or url must be defined
version = "1.0.0" -- optional version = "1.0.0" -- optional, config options listed below
}) }
itchy:check_version(game)
newVersion = love.thread.getChannel("itchy") -- somewhere where this will be called periodically
if newVersion:getCount() > 0 then local data = itchy:new_version(game) -- passing the game table is not necessary
local data = newVersion:demand() if data then
-- easiest usage is to just print something like this to the user -- easiest usage, just print the message to the user
print("Version: 1.0.0 Latest version: " .. data.message) love.graphics.print("Version: 1.0.0 Latest: " .. data.message)
end end