This site has been acquired by Toptal
(Attention! API endpoint has changed)
Save New Duplicate & Edit Just Text1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
local imgquant = require 'imgquant'
local bson = require 'bson'
local config = require 'mineboy_config'
local monitor = peripheral.wrap(config.screenMonitor)
monitor.setTextScale(0.5)
local controlMonitor = peripheral.wrap(config.controlMonitor.monitorId)
-- Pretty color logging
local log = {
rawLog = function(color, text, message)
term.setTextColor(color)
write('[' .. text .. '] ')
term.setTextColor(colors.gray)
write(message .. '\n')
end
}
log.info = function(message)
log.rawLog(colors.blue, "INFO", message)
end
log.error = function(message)
log.rawLog(colors.red, "ERROR", message)
end
log.warn = function(message)
log.rawLog(colors.yellow, "WARN", message)
end
log.query = function(message)
log.rawLog(colors.green, "QUERY", message)
term.setTextColor(colors.gray)
write('> ')
term.setTextColor(colors.white)
return read()
end
-- Length of a table regardless of what the indexes are
function tableLen(T)
local count = 0
for _ in pairs(T) do
count = count + 1
end
return count
end
log.info('Getting games list from server')
local gameList = bson.decode(http.get(config.httpUrl .. '/listGames').readAll())
log.info('Retrieved ' .. tableLen(gameList) .. ' games: ' .. textutils.serialize(gameList))
local ws, err = http.websocket(config.wsUrl .. '/attach')
if err then
log.error('Unable to open websocket: ' .. err)
return
else
log.info('Connected to socket: ' .. config.wsUrl)
end
-- Controller button map
local buttons = {
{
x = 2,
y = 5,
height = 2,
width = 3,
button = 'LEFT'
},
{
x = 10,
y = 5,
height = 2,
width = 3,
button = 'RIGHT'
},
{
x = 6,
y = 2,
height = 2,
width = 3,
button = 'UP'
},
{
x = 6,
y = 8,
height = 2,
width = 3,
button = 'DOWN'
},
{
x = 35,
y = 4,
height = 2,
width = 3,
button = 'A'
},
{
x = 30,
y = 6,
height = 2,
width = 3,
button = 'B'
},
{
x = 15,
y = 10,
height = 1,
width = 5,
button = 'SELECT'
},
{
x = 22,
y = 10,
height = 1,
width = 5,
button = 'START'
}
}
function buttons:render()
term.redirect(controlMonitor)
term.setBackgroundColor(colors[config.controlMonitor.backgroundColor])
term.clear()
for _, button in ipairs(self) do
paintutils.drawFilledBox(
button.x,
button.y,
button.x + button.width,
button.y + button.height,
colors[config.controlMonitor.foregroundColor])
end
term.redirect(term.native())
end
function buttons:updateButtons(x, y)
for _, button in ipairs(self) do
if button.x <= x and x <= button.x + button.width and button.y <= y and y <= button.y + button.height then
ws.send(bson.encode({
type = 'PRESS_BUTTON',
button = button.button
}))
end
end
end
--- Render the received frame to the screen
-- @param width of the image
-- @param height of the image
-- @param color palette used to render the image (length of 16)
-- @param screen to render, array of 4-bit values (0-16)
local function blitFrame(width, height, palette, screen)
-- Extract image data
local pos = 1
local img = {}
for y = 1, height do
img[y] = {}
for x = 1, width, 2 do
local c = screen:byte(pos)
pos = pos + 1
img[y][x] = bit32.rshift(c, 4) + 1
img[y][x + 1] = bit32.band(c, 15) + 1
end
end
local ccImage = imgquant.toCCImage(img, palette)
imgquant.drawBlitImage(1, 1, ccImage, palette, monitor)
end
--- Select a game and send a query to play the game
local function selectGame()
local index
while true do
index = tonumber(log.query('Select a game to play (0-' .. tableLen(gameList) - 1 .. ')'))
if index then
break
else
log.error('Input must be a number')
end
end
ws.send(bson.encode({
type = 'SELECT_GAME',
index = index
}))
local res = bson.decode(ws.receive())
log.info('Started game: ' .. res.name)
end
local function gameLoop()
while true do
-- Request draw from server
ws.send(bson.encode({
type = 'REQUEST_DRAW'
}))
-- Wait for frame
while true do
local event = { os.pullEvent() }
if event[1] == 'websocket_message' then
local message = bson.decode(event[3])
if message.type == 'ERROR' then
-- Message we recieved was an error of some sort, log it
log.error(response.error)
elseif message.type == 'SCREEN_DRAW' then
blitFrame(message.width, message.height, message.palette, message.screen)
-- Break out of the listener loop so we can request another frame
break
end
elseif event[1] == 'monitor_touch' then
buttons:updateButtons(event[3], event[4])
elseif event[1] == 'key' then
local character = event[2]
print(character)
-- Exit game session
if character == keys.x then
ws.send(bson.encode({ type = 'EXIT_GAME' }))
while true do
local res = bson.decode(ws.receive())
if res.type == 'GAME_EXITED' then
break
end
end
log.info('Game session was terminated')
end
end
end
end
end
selectGame()
buttons:render()
gameLoop()