The World Mosquito Program (WMP) modifies mosquitoes with a bacteria — Wolbachia. This reduces their ability to carry deadly viruses. (It makes me perversely happy that we’re infecting mosquitoes now đ.)
Modifying mosquitoes is an expensive process. With a limited set of âgood mosquitoesâ, it is critical to find the best release points that will help them replicate rapidly.
But planning the release points took weeks of manual effort. It involved ground personnel going through several iterations.
So our team took high-resolution satellite images, figured out the building density, estimated population density based on that, and generated a release plan. This model is 70% more accurate and reduced the time from 3 weeks to 2 hours.
Minecraft lets you connect to a websocket server when you’re in a game. The server can receive and send any commands. This lets you build a bot that you can … (well, I don’t know what it can do, let’s explore.)
Minecraft has commands you can type on a chat window. For example, type / to start a command and type setblock ~1 ~0 ~0 grass changes the block 1 north of you into grass. (~ means relative to you. Coordinates are specified as X, Y and Z.)
Note: These instructions were tested on Minecraft Bedrock 1.16. I haven’t tested them on the Java Edition.
Connect to Minecraft
You can send any command to Minecraft from a websocket server. Let’s use JavaScript for this.
First, run npm install ws uuid. (We need ws for websockets and uuid to generate unique IDs.)
const WebSocket = require('ws')
const uuid = require('uuid') // For later use
// Create a new websocket server on port 3000
console.log('Ready. On MineCraft chat, type /connect localhost:3000')
const wss = new WebSocket.Server({ port: 3000 })
// On Minecraft, when you type "/connect localhost:3000" it creates a connection
wss.on('connection', socket => {
console.log('Connected')
})
On Minecraft > Settings > General > Profile, turn off the “Require Encrypted Websockets” setting.
Run node mineserver1.js. Then type /connect localhost:3000 in a Minecraft chat window. You’ll see 2 things:
MineCraft says “Connection established to server: ws://localhost:3000”
Node prints “Connected”
Now, our program is connected to Minecraft, and can send/receive messages.
Notes:
The Python equivalent is in mineserver1.py. Run python mineserver1.py.
If you get an Uncaught Error: Cannot find module 'ws', make sure you ran npm install ws uuid.
If you get an “Encrypted Session Required” error, make sure you turned off the “Require Encrypted Websockets” setting mentioned above.
To disconnect, run /connect off
Subscribe to chat messages
Now let’s listen to the players’ chat.
A connected websocket server can send a “subscribe” message to Minecraft saying it wants to “listen” to specific actions. For example, you can subscribe to “PlayerMessage”. Whenever a player sents a chat message, Minecraft will notify the websocket client.
Here’s how to do that. Add this code in the wss.on('connection', socket => { ... }) function.
// Tell Minecraft to send all chat messages. Required once after Minecraft starts
socket.send(JSON.stringify({
"header": {
"version": 1, // We're using the version 1 message protocol
"requestId": uuid.v4(), // A unique ID for the request
"messageType": "commandRequest", // This is a request ...
"messagePurpose": "subscribe" // ... to subscribe to ...
},
"body": {
"eventName": "PlayerMessage" // ... all player messages.
},
}))
Now, every time a player types something in the chat window, the socket will receive it. Add this code below the above code:
// When MineCraft sends a message (e.g. on player chat), print it.
socket.on('message', packet => {
const msg = JSON.parse(packet)
console.log(msg)
})
This code parses all the messages it receives and prints them.
This code in is mineserver2.js. Run node mineserver2.js. Then type /connect localhost:3000 in a Minecraft chat window. Then type a message (e.g. “alpha”) in the chat window. You’ll see a message like this in the console.
{
header: {
messagePurpose: 'event', // This is an event
requestId: '00000000-0000-0000-0000-000000000000',
version: 1 // using version 1 message protocol
},
body: {
eventName: 'PlayerMessage',
measurements: null,
properties: {
AccountType: 1,
ActiveSessionID: 'e0afde71-9a15-401b-ba38-82c64a94048d',
AppSessionID: 'b2f5dddc-2a2d-4ec1-bf7b-578038967f9a',
Biome: 1, // Plains Biome. https://minecraft.gamepedia.com/Biome
Build: '1.16.201', // That's my build
BuildNum: '5131175',
BuildPlat: 7,
Cheevos: false,
ClientId: 'fcaa9859-0921-348e-bc7c-1c91b72ccec1',
CurrentNumDevices: 1,
DeviceSessionId: 'b2f5dddc-2a2d-4ec1-bf7b-578038967f9a',
Difficulty: 'NORMAL', // I'm playing on normal difficulty
Dim: 0,
GlobalMultiplayerCorrelationId: '91967b8c-01c6-4708-8a31-f111ddaa8174',
Message: 'alpha', // This is the message I typed
MessageType: 'chat', // It's of type chat
Mode: 1,
NetworkType: 0,
Plat: 'Win 10.0.19041.1',
PlayerGameMode: 1, // Creative. https://minecraft.gamepedia.com/Commands/gamemode
Sender: 'Anand', // That's me.
Seq: 497,
WorldFeature: 0,
WorldSessionId: '8c9b4d3b-7118-4324-ba32-c357c709d682',
editionType: 'win10',
isTrial: 0,
locale: 'en_IN',
vrMode: false
}
}
}
Notes:
The Python equivalent is in mineserver2.py. Run python mineserver2.py.
The full list of things we can subscribe to is undocumented, but @jocopa3 has reverse-engineered a list of messages we can subscribe to, and they’re somewhat meaningful.
Let’s create a pyramid of size 10 around us when we type pyramid 10 in the chat window.
The first step is to check if the player sent a chat message like pyramid 10 (or another number). Add this code below the above code:
// When MineCraft sends a message (e.g. on player chat), act on it.
socket.on('message', packet => {
const msg = JSON.parse(packet)
// If this is a chat window
if (msg.body.eventName === 'PlayerMessage') {
// ... and it's like "pyramid 10" (or some number), draw a pyramid
const match = msg.body.properties.Message.match(/^pyramid (\d+)/i)
if (match)
draw_pyramid(+match[1])
}
})
If the user types “pyramid 3” on the chat window, draw_pyramid(3) is called.
In draw_pyramid(), let’s send commands to build a pyramid. To send a command, we need to create a JSON with the command (e.g. setblock ~1 ~0 ~0 grass). Add this code below the above code:
function send(cmd) {
const msg = {
"header": {
"version": 1,
"requestId": uuid.v4(), // Send unique ID each time
"messagePurpose": "commandRequest",
"messageType": "commandRequest"
},
"body": {
"version": 1, // TODO: Needed?
"commandLine": cmd, // Define the command
"origin": {
"type": "player" // Message comes from player
}
}
}
socket.send(JSON.stringify(msg)) // Send the JSON string
}
Let’s write draw_pyramid() to create a pyramid using glowstone by adding this code below the above code:
// Draw a pyramid of size "size" around the player.
function draw_pyramid(size) {
// y is the height of the pyramid. Start with y=0, and keep building up
for (let y = 0; y < size + 1; y++) {
// At the specified y, place blocks in a rectangle of size "side"
let side = size - y;
for (let x = -side; x < side + 1; x++) {
send(`setblock ~${x} ~${y} ~${-side} glowstone`)
send(`setblock ~${x} ~${y} ~${+side} glowstone`)
send(`setblock ~${-side} ~${y} ~${x} glowstone`)
send(`setblock ~${+side} ~${y} ~${x} glowstone`)
}
}
}
Then type /connect localhost:3000 in a Minecraft chat window.
Then type pyramid 3 in the chat window.
You’ll be surrounded by a glowstone pyramid, and the console will show every command response.
Notes on common error messages:
The block couldn't be placed (-2147352576): The same block was already at that location.
Syntax error: Unexpected "xxx": at "~0 ~9 ~-1 >>xxx<<" (-2147483648): You gave wrong arguments to the command.
Too many commands have been requested, wait for one to be done (-2147418109): Minecraft only allows 100 commands can be executed without waiting for their response.
Typing “pyramid 3” works just fine. But try “pyramid 5” and your pyramid is incomplete.
That’s because Minecraft only allows up to 100 messages in its queue. On the 101st message, you get a Too many commands have been requested, wait for one to be done error.
{
"header": {
"version": 1,
"messagePurpose": "error",
"requestId": "a5051664-e9f4-4f9f-96b8-a56b5783117b"
},
"body": {
"statusCode": -2147418109,
"statusMessage": "Too many commands have been requested, wait for one to be done"
}
}
So let’s modify send() to add to a queue and send in batches. We’ll create two queues:
const sendQueue = [] // Queue of commands to be sent
const awaitedQueue = {} // Queue of responses awaited from Minecraft
In wss.on('connection', ...), when Minecraft completes a command, we’ll remove it from the awaitedQueue. If the command has an error, we’ll report it.
// If we get a command response
if (msg.header.messagePurpose == 'commandResponse') {
// ... and it's for an awaited command
if (msg.header.requestId in awaitedQueue) {
// Print errors 5(if any)
if (msg.body.statusCode < 0)
console.log(awaitedQueue[msg.header.requestId].body.commandLine, msg.body.statusMessage)
// ... and delete it from the awaited queue
delete awaitedQueue[msg.header.requestId]
}
}
// Now, we've cleared all completed commands from the awaitedQueue.
Once we’ve processed Minecraft’s response, we’ll send pending messages from sendQueue, upto 100 and add them to the awaitedQueue.
// We can send new commands from the sendQueue -- up to a maximum of 100.
let count = Math.min(100 - Object.keys(awaitedQueue).length, sendQueue.length)
for (let i = 0; i < count; i++) {
// Each time, send the first command in sendQueue, and add it to the awaitedQueue
let command = sendQueue.shift()
socket.send(JSON.stringify(command))
awaitedQueue[command.header.requestId] = command
}
// Now we've sent as many commands as we can. Wait till the next PlayerMessage/commandResponse
Finally, in function send(), instead of socket.send(JSON.stringify(msg)), we use sendQueue.push(msg) to add the message to the queue.
With Office 365, PowerPoint supports SVG editing. This is really powerful. It means you can draw in PowerPoint and render it on the web — including as interactive or animated visuals.
For example, the SVG in this simulator was created just with PowerPoint.
The process is simple. Draw anything. Select any shapes and right-click. Select Save As Picture… and choose SVG.
For example, you can use PowerPoint to create Smart Art, export it as SVG, and embed it into a page. See this example on CodePen.
The SVG is fairly well structured and easy to edit. The code generated for these 2 simple shapes:
… is quite straight-forward — just two SVG shapes.
I was worried about the lack of SVG authoring tools in Windows. (InkScape is not usable, and Adobe’s tools are complex and expensive.) PowerPoint fits perfectly.
Given the blazing speed of Node.js these days, I expected HTML parsing to be faster on Node than on Python.
So I compared lxml with htmlparser2 — the fastest libraries on Python and JS in parsing the reddit home page (~700KB).
lxml took ~8.6 milliseconds
htmlparser2 took ~14.5 milliseconds
Looks like lxml is much faster. I’m likely to stick around with Python for pure HTML parsing (without JavaScript) for a while longer.
In [1]: from lxml.html import parse
In [2]: %timeit tree = parse('reddit.html')
8.69 ms Âą 190 Âľs per loop (mean Âą std. dev. of 7 runs, 100 loops each)
const { Parser } = require("htmlparser2");
const { DomHandler } = require("domhandler");
const fs = require('fs');
const html = fs.readFileSync('reddit.html');
const handler = new DomHandler(function (error, dom) { });
const start = +new Date();
for (var i = 0; i < 100; i++) {
const parser = new Parser();
parser.write(html);
parser.end();
}
const end = +new Date();
console.log((end - start) / 100);
Note: If I run the htmlparser2 code 100 times instead of 10, it only takes 7ms per loop. The more the number of loops, the faster it parses. I guess Node.js optimizes repeated loops. But I’m only interested in the first iteration, since I’ll be parsing files only once.
Itâs easy to convert addresses into latitudes and longitudes into addresses in Excel. Here’s the Github project with a downloadable Excel file.
This is via Visual Basic code for a GoogleGeocode function that geocodes addresses.
Function GoogleGeocode(address As String) As String
Dim xDoc As New MSXML2.DOMDocument
xDoc.async = False
xDoc.Load ("http://maps.googleapis.com/maps/api/geocode/" + _
"xml?address=" + address + "&sensor=false")
If xDoc.parseError.ErrorCode <> 0 Then
GoogleGeocode = xDoc.parseError.reason
Else
xDoc.setProperty "SelectionLanguage", "XPath"
lat = xDoc.SelectSingleNode("//lat").Text
lng = xDoc.SelectSingleNode("//lng").Text
GoogleGeocode = lat & "," & lng
End If
End Function
A Python library for phylogenetics and phylogenetic computing: reading, writing, simulation, processing and manipulation of phylogenetic trees (phylogenies) and characters.
After watching Bret Victorâs Inventing on Principle, I just had to figure out a way of getting live reloading to work. I know about LiveReload, of course, and everything Iâve heard about it is good. But their Windows version is in alpha, and Iâm not about to experiment just yet.
There are no dependencies on any library, like jQuery. However, it requires that the file be on a web server. (Itâs easy to fix that, but since I always run a local webserver, Iâll let you solve that problem yourself.)
Hereâs the easiest way to set up a Windows XP virtual machine that I could find.
(This is useful if you want to try out programs without installing it on your main machine; test your code on a new machine; or test your website on IE6 / IE7 / IE8.)
Go to the Virtual PC download site. (I tried VirtualBox and VMWare Player. Virtual PC is better if youâre running Windows on Windows.) If you have Windows 7 Starter or Home, select “Donât need XP Mode and want VPC only? Download Windows Virtual PC without Windows XP Mode.” If you have Windows Vista or Windows 7, select âLooking for Virtual PC 2007?â
Download it. (You may have to jump through a few hoops like activation.)
Download Windows XP and run it to extract the files. (Itâs a 400MB download.)
Open the âWindows XP.vmcâ file â just double-clicking ought to work. At this point, you have a working Windows XP version. (The Administrator password is âPassword1â.)
Thatâs pretty much it. Youâve got a Windows XP machine running inside your other Windows machine.
Update (18 Sep 2012): I noticed something weird. The memory usage of VMWindow and vpc.exe is tiny!
Between the two processes, they take up less than 30MB of memory. This is despite the Windows XP Task Manager inside the virtual machine showing me 170MB of usage. Iâve no clue whatâs happening, but am beginning to enjoy virtualisation. Iâll start up a few more machines, and perhaps install a database cluster across them.