diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..a4f74e6 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,16 @@ +version: 2 + +jobs: + build: + docker: + - image: circleci/node:carbon + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - run: npm i + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - ./node_modules + - run: npm test diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 7fa19bc..0000000 --- a/circle.yml +++ /dev/null @@ -1,3 +0,0 @@ -machine: - node: - version: 8 diff --git a/package.json b/package.json index fde1ef6..d4f6fe0 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "scripts": { "prepare": "require-self", - "test": "mocha --reporter spec" + "test": "jest --verbose --runInBand" }, "keywords": [], "licenses": { @@ -44,7 +44,6 @@ "range": "^0.0.3", "request": "^2.83.0", "request-promise": "^4.1.0", - "requireindex": "~1.1.0", "spiralloop": "^1.0.2", "vec3": "^0.1.3" }, @@ -57,10 +56,9 @@ "url": "http://github.com/PrismarineJS/flying-squid/issues" }, "devDependencies": { - "chai": "^3.2.0", - "longjohn": "^0.2.8", + "jest": "^22.1.4", + "longjohn": "^0.2.12", "mineflayer": "^2.6.1", - "mocha": "^3.0.0", - "require-self": "^0.1.0" + "require-self": "^0.2.1" } } diff --git a/src/index.js b/src/index.js index d002739..37a1232 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ const mc = require('minecraft-protocol'); const EventEmitter = require('events').EventEmitter; const path = require('path'); -const requireIndex = require('requireindex'); +const requireIndex = require('./lib/requireindex'); require('emit-then').register(); if (process.env.NODE_ENV === 'dev'){ require('longjohn'); @@ -33,7 +33,7 @@ class MCServer extends EventEmitter { connect(options) { const plugins = requireIndex(path.join(__dirname, 'lib', 'plugins')); - this._server = mc.createServer(options); + this._server = mc.createServer(options); Object.keys(plugins) .filter(pluginName => plugins[pluginName].server!=undefined) .forEach(pluginName => plugins[pluginName].server(this, options)); @@ -42,4 +42,4 @@ class MCServer extends EventEmitter { this._server.on('listening', () => this.emit('listening',this._server.socketServer.address().port)); this.emit('asap'); } -} \ No newline at end of file +} diff --git a/src/lib/plugins/login.js b/src/lib/plugins/login.js index 3976038..0ffc947 100644 --- a/src/lib/plugins/login.js +++ b/src/lib/plugins/login.js @@ -1,7 +1,7 @@ const Vec3 = require("vec3").Vec3; const path = require('path'); -const requireIndex = require('requireindex'); +const requireIndex = require('../requireindex'); const plugins = requireIndex(path.join(__dirname,'..', 'plugins')); const Command = require('flying-squid').Command; diff --git a/src/lib/plugins/spawn.js b/src/lib/plugins/spawn.js index cf09621..fee33f3 100644 --- a/src/lib/plugins/spawn.js +++ b/src/lib/plugins/spawn.js @@ -4,7 +4,7 @@ const mobsById=require("minecraft-data")(version).mobs; const objectsById=require("minecraft-data")(version).objects; const Entity = require("prismarine-entity"); const path = require('path'); -const requireIndex = require('requireindex'); +const requireIndex = require('../requireindex'); const plugins = requireIndex(path.join(__dirname,'..', 'plugins')); const Item = require("prismarine-item")(version); const UserError = require('flying-squid').UserError; @@ -323,4 +323,4 @@ module.exports.entity=function(entity,serv) { entity._writeOthersNearby('attach_entity',p); } -}; \ No newline at end of file +}; diff --git a/src/lib/requireindex.js b/src/lib/requireindex.js new file mode 100644 index 0000000..5940326 --- /dev/null +++ b/src/lib/requireindex.js @@ -0,0 +1,59 @@ +// Adapted from https://github.com/stephenhandley/requireindex (under the MIT license) + +const fs = require('fs') +const path = require('path') + +module.exports = function(dir, basenames) { + let requires = {} + + if (arguments.length === 2) { + // If basenames argument is passed, explicitly include those files + basenames.forEach(function (basename) { + let filepath = path.resolve(path.join(dir, basename)) + requires[basename] = require(filepath) + }) + } else if (arguments.length === 1) { + // If basenames arguments isn't passed, require all JavaScript + // Files (except for those prefixed with _) and all directories + + let files = fs.readdirSync(dir) + + // Sort files in lowercase alpha for Linux + files.sort((a, b) => { + a = a.toLowerCase() + b = b.toLowerCase() + + if (a < b) { + return -1 + } else if (b < a) { + return 1 + } else { + return 0 + } + }) + + files.forEach(filename => { + // Ignore `index.js` and files prefixed with underscore and + if ((filename === 'index.js') || (filename[0] === '_') || (filename[0] === '.')) { + return + } + + let filepath = path.resolve(path.join(dir, filename)) + let ext = path.extname(filename) + let stats = fs.statSync(filepath) + + // Don't require non-javascript files (.txt, .md, etc.) + if (stats.isFile() && !(['.js', '.node', '.json'].includes(ext))) { + return + } + + let basename = path.basename(filename, ext) + + requires[basename] = require(filepath) + }) + } else { + throw new Error('Must pass directory as first argument') + } + + return requires +} diff --git a/test/mineflayer.js b/test/mineflayer.js deleted file mode 100644 index b55b4aa..0000000 --- a/test/mineflayer.js +++ /dev/null @@ -1,248 +0,0 @@ -const net = require('net'); -const mcServer=require("flying-squid"); -const settings = require('../config/default-settings'); -const mineflayer = require("mineflayer"); -const assert = require('chai').assert; -const Vec3 = require('vec3').Vec3; -const Item = require('prismarine-item')("1.8"); - -function assertPosEqual(actual,expected) { - assert.isBelow(actual.distanceTo(expected),1,"expected: "+expected+", actual: "+actual+"\n"); -} -const once = require('event-promise'); - -describe("Server with mineflayer connection", function() { - this.timeout(60 * 1000); - let bot; - let bot2; - let serv; - - async function onGround(bot) - { - await new Promise((cb) => { - const l=() => { - if(bot.entity.onGround) { - bot.removeListener("move",l); - cb(); - } - }; - bot.on("move",l); - }); - } - - async function waitMessage(bot,message) { - const msg1=await once(bot,'message'); - assert.equal(msg1.extra[0].text,message); - } - - async function waitMessages(bot,messages) { - const toReceive=messages.reduce((acc,message) => { - acc[message]=1; - return acc; - },{}); - const received={}; - return new Promise(cb => { - const listener=msg => { - const message=msg.extra[0].text; - if(!toReceive[message]) throw new Error("Received "+message+" , expected to receive one of "+messages); - if(received[message]) throw new Error("Received "+message+" two times"); - received[message]=1; - if(Object.keys(received).length==messages.length) - { - bot.removeListener('message',listener); - cb(); - } - }; - bot.on('message',listener); - }); - } - - async function waitLoginMessage(bot) { - return Promise.all([waitMessages(bot,['bot joined the game.','bot2 joined the game.'])]); - } - - beforeEach(async function () { - this.timeout(60 * 1000); - const options = settings; - options["online-mode"]=false; - options["port"]=25566; - options["view-distance"]=2; - options["worldFolder"]=undefined; - - serv=mcServer.createMCServer(options); - - await once(serv,"listening"); - bot = mineflayer.createBot({ - host: "localhost", - port: 25566, - username: "bot", - version: "1.8" - }); - bot2 = mineflayer.createBot({ - host: "localhost", - port: 25566, - username: "bot2", - version: "1.8" - }); - - await Promise.all([once(bot,'login'),once(bot2,'login')]); - bot.entity.onGround=false; - bot2.entity.onGround=false; - }); - - afterEach(async () => { - await serv.quit(); - }); - - describe("actions",() => { - - function waitSpawnZone(bot,view) - { - const nbChunksExpected=(view*2)*(view*2); - let c=0; - return new Promise(cb => { - const listener=() => { - c++; - if(c==nbChunksExpected) - { - bot.removeListener('chunkColumnLoad',listener); - cb(); - } - }; - bot.on('chunkColumnLoad',listener); - }); - } - - it("can dig",async function () { - this.timeout(60 * 1000); - await Promise.all([waitSpawnZone(bot,2),waitSpawnZone(bot2,2),onGround(bot),onGround(bot2)]); - - const pos=bot.entity.position.offset(0,-1,0).floored(); - bot.dig(bot.blockAt(pos)); - - let [,newBlock]=await once(bot2,'blockUpdate',{array:true}); - assertPosEqual(newBlock.position,pos); - assert.equal(newBlock.type,0,"block "+pos+" should have been dug"); - }); - - - it("can place a block",async function () { - this.timeout(60 * 1000); - await Promise.all([waitSpawnZone(bot,2),waitSpawnZone(bot2,2),onGround(bot),onGround(bot2)]); - - const pos=bot.entity.position.offset(0,-2,0).floored(); - bot.dig(bot.blockAt(pos)); - - let [oldBlock,newBlock]=await once(bot2,'blockUpdate',{array:true}); - assertPosEqual(newBlock.position,pos); - assert.equal(newBlock.type,0,"block "+pos+" should have been dug"); - - bot.creative.setInventorySlot(36, new Item(1,1)); - await new Promise((cb) => { - bot.inventory.on("windowUpdate",(slot,oldItem,newItem) => { - if(slot==36 && newItem && newItem.type==1) - cb(); - }); - }); - - bot.placeBlock(bot.blockAt(pos.offset(0,-1,0)),new Vec3(0,1,0)); - - [oldBlock,newBlock]=await once(bot2,'blockUpdate',{array:true}); - assertPosEqual(newBlock.position,pos); - assert.equal(newBlock.type,1,"block "+pos+" should have been placed"); - }); - }); - - describe("commands",() => { - - it("has an help command", async () => { - await waitLoginMessage(bot); - bot.chat("/help"); - await once(bot,"message"); - }); - it("can use /particle",async () => { - bot.chat("/particle 5 10 100 100 100"); - await once(bot._client,'world_particles'); - }); - it("can use /playsound",async () => { - bot.chat('/playsound ambient.weather.rain'); - await once(bot,'soundEffectHeard'); - }); - - function waitDragon() - { - return new Promise((done) => { - const listener=(entity) => { - if(entity.name=="EnderDragon") { - bot.removeListener('entitySpawn',listener); - done(); - } - }; - bot.on('entitySpawn',listener); - }); - } - - it("can use /summon",async () => { - bot.chat('/summon EnderDragon'); - await waitDragon(); - }); - it("can use /kill",async () => { - bot.chat('/summon EnderDragon'); - await waitDragon(); - bot.chat('/kill @e[type=EnderDragon]'); - const entity=await once(bot,'entityDead'); - assert.equal(entity.name,"EnderDragon"); - }); - describe("can use /tp",() => { - it("can tp myself", async () => { - bot.chat('/tp 2 3 4'); - await once(bot,'forcedMove'); - assertPosEqual(bot.entity.position, new Vec3(2, 3, 4)); - }); - it("can tp somebody else",async () => { - bot.chat('/tp bot2 2 3 4'); - await once(bot2,'forcedMove'); - assertPosEqual(bot2.entity.position, new Vec3(2, 3, 4)); - }); - it("can tp to somebody else",async () => { - await onGround(bot); - bot.chat('/tp bot2 bot'); - await once(bot2,'forcedMove'); - assertPosEqual(bot2.entity.position, bot.entity.position); - }); - it("can tp with relative positions",async () => { - await onGround(bot); - const initialPosition=bot.entity.position.clone(); - bot.chat('/tp ~1 ~-2 ~3'); - await once(bot,'forcedMove'); - assertPosEqual(bot.entity.position,initialPosition.offset(1,-2,3)); - }); - it("can tp somebody else with relative positions",async () => { - await Promise.all([onGround(bot),onGround(bot2)]); - const initialPosition=bot2.entity.position.clone(); - bot.chat('/tp bot2 ~1 ~-2 ~3'); - await once(bot2,'forcedMove'); - assertPosEqual(bot2.entity.position,initialPosition.offset(1,-2,3)); - }); - }); - it("can use /deop",async () => { - await waitLoginMessage(bot); - bot.chat('/deop bot'); - await waitMessage(bot,'bot is deopped'); - bot.chat('/op bot'); - await waitMessage(bot,'You do not have permission to use this command'); - serv.getPlayer("bot").op=true; - }); - it("can use /setblock",async() => { - await once(bot, 'chunkColumnLoad'); - bot.chat('/setblock 1 2 3 95 0'); - let [,newBlock]=await once(bot,'blockUpdate:'+new Vec3(1,2,3),{array:true}); - assert.equal(newBlock.type,95); - }); - it("can use /xp",async() => { - bot.chat('/xp 100'); - await once(bot,"experience"); - assert.equal(bot.experience.points,100); - }); - }); -}); diff --git a/test/mineflayer.test.js b/test/mineflayer.test.js new file mode 100644 index 0000000..d19b670 --- /dev/null +++ b/test/mineflayer.test.js @@ -0,0 +1,238 @@ +const net = require('net') +const squid = require('flying-squid') +const settings = require('../config/default-settings') +const mineflayer = require('mineflayer') +const { Vec3 } = require('vec3') +const Item = require('prismarine-item')('1.8') + +function assertPosEqual (actual, expected) { + expect(actual.distanceTo(expected)).toBeLessThan(1) +} + +const once = require('event-promise') + +describe('server with mineflayer connection', () => { + jest.setTimeout(60 * 1000) + let bot + let bot2 + let serv + + async function onGround (bot) { + await new Promise((cb) => { + const l = () => { + if (bot.entity.onGround) { + bot.removeListener('move', l) + cb() + } + } + bot.on('move', l) + }) + } + + async function waitMessage (bot, message) { + const msg1 = await once(bot, 'message') + expect(msg1.extra[0].text).toEqual(message) + } + + async function waitMessages (bot, messages) { + const toReceive = messages.reduce((acc, message) => { + acc[message] = 1 + return acc + }, {}) + const received = {} + return new Promise(cb => { + const listener = msg => { + const message = msg.extra[0].text + if (!toReceive[message]) throw new Error('Received ' + message + ' , expected to receive one of ' + messages) + if (received[message]) throw new Error('Received ' + message + ' two times') + received[message] = 1 + if (Object.keys(received).length == messages.length) { + bot.removeListener('message', listener) + cb() + } + } + bot.on('message', listener) + }) + } + + async function waitLoginMessage (bot) { + return Promise.all([waitMessages(bot, ['bot joined the game.', 'bot2 joined the game.'])]) + } + + beforeEach(async () => { + const options = settings + options['online-mode'] = false + options['port'] = 25566 + options['view-distance'] = 2 + options['worldFolder'] = undefined + options['logging'] = false + + serv = squid.createMCServer(options) + + await once(serv, 'listening') + bot = mineflayer.createBot({ + host: 'localhost', + port: 25566, + username: 'bot', + version: '1.8' + }) + bot2 = mineflayer.createBot({ + host: 'localhost', + port: 25566, + username: 'bot2', + version: '1.8' + }) + + await Promise.all([once(bot, 'login'), once(bot2, 'login')]) + bot.entity.onGround = false + bot2.entity.onGround = false + }) + + afterEach(async () => { + await serv.quit() + }) + + describe('actions', () => { + function waitSpawnZone (bot, view) { + const nbChunksExpected = (view * 2) * (view * 2) + let c = 0 + return new Promise(cb => { + const listener = () => { + c++ + if (c == nbChunksExpected) { + bot.removeListener('chunkColumnLoad', listener) + cb() + } + } + bot.on('chunkColumnLoad', listener) + }) + } + + test('can dig', async () => { + await Promise.all([waitSpawnZone(bot, 2), waitSpawnZone(bot2, 2), onGround(bot), onGround(bot2)]) + + const pos = bot.entity.position.offset(0, -1, 0).floored() + bot.dig(bot.blockAt(pos)) + + let [, newBlock] = await once(bot2, 'blockUpdate', {array: true}) + assertPosEqual(newBlock.position, pos) + expect(newBlock.type).toEqual(0) + }) + + test('can place a block', async () => { + await Promise.all([waitSpawnZone(bot, 2), waitSpawnZone(bot2, 2), onGround(bot), onGround(bot2)]) + + const pos = bot.entity.position.offset(0, -2, 0).floored() + bot.dig(bot.blockAt(pos)) + + let [oldBlock, newBlock] = await once(bot2, 'blockUpdate', {array: true}) + assertPosEqual(newBlock.position, pos) + expect(newBlock.type).toEqual(0) + + bot.creative.setInventorySlot(36, new Item(1, 1)) + await new Promise((cb) => { + bot.inventory.on('windowUpdate', (slot, oldItem, newItem) => { + if (slot == 36 && newItem && newItem.type == 1) { cb() } + }) + }) + + bot.placeBlock(bot.blockAt(pos.offset(0, -1, 0)), new Vec3(0, 1, 0)); + + [oldBlock, newBlock] = await once(bot2, 'blockUpdate', {array: true}) + assertPosEqual(newBlock.position, pos) + expect(newBlock.type).toEqual(1) + }) + }) + + describe('commands', () => { + jest.setTimeout(10 * 1000) + test('has an help command', async () => { + await waitLoginMessage(bot) + bot.chat('/help') + await once(bot, 'message') + }) + test('can use /particle', async () => { + bot.chat('/particle 5 10 100 100 100') + await once(bot._client, 'world_particles') + }) + test('can use /playsound', async () => { + bot.chat('/playsound ambient.weather.rain') + await once(bot, 'soundEffectHeard') + }) + + function waitDragon () { + return new Promise((done) => { + const listener = (entity) => { + if (entity.name == 'EnderDragon') { + bot.removeListener('entitySpawn', listener) + done() + } + } + bot.on('entitySpawn', listener) + }) + } + + test('can use /summon', async () => { + bot.chat('/summon EnderDragon') + await waitDragon() + }) + test('can use /kill', async () => { + bot.chat('/summon EnderDragon') + await waitDragon() + bot.chat('/kill @e[type=EnderDragon]') + const entity = await once(bot, 'entityDead') + expect(entity.name).toEqual('EnderDragon') + }) + describe('can use /tp', () => { + test('can tp myself', async () => { + bot.chat('/tp 2 3 4') + await once(bot, 'forcedMove') + assertPosEqual(bot.entity.position, new Vec3(2, 3, 4)) + }) + test('can tp somebody else', async () => { + bot.chat('/tp bot2 2 3 4') + await once(bot2, 'forcedMove') + assertPosEqual(bot2.entity.position, new Vec3(2, 3, 4)) + }) + test('can tp to somebody else', async () => { + await onGround(bot) + bot.chat('/tp bot2 bot') + await once(bot2, 'forcedMove') + assertPosEqual(bot2.entity.position, bot.entity.position) + }) + test('can tp with relative positions', async () => { + await onGround(bot) + const initialPosition = bot.entity.position.clone() + bot.chat('/tp ~1 ~-2 ~3') + await once(bot, 'forcedMove') + assertPosEqual(bot.entity.position, initialPosition.offset(1, -2, 3)) + }) + test('can tp somebody else with relative positions', async () => { + await Promise.all([onGround(bot), onGround(bot2)]) + const initialPosition = bot2.entity.position.clone() + bot.chat('/tp bot2 ~1 ~-2 ~3') + await once(bot2, 'forcedMove') + assertPosEqual(bot2.entity.position, initialPosition.offset(1, -2, 3)) + }) + }) + test('can use /deop', async () => { + await waitLoginMessage(bot) + bot.chat('/deop bot') + await waitMessage(bot, 'bot is deopped') + bot.chat('/op bot') + await waitMessage(bot, 'You do not have permission to use this command') + serv.getPlayer('bot').op = true + }) + test('can use /setblock', async () => { + await once(bot, 'chunkColumnLoad') + bot.chat('/setblock 1 2 3 95 0') + let [, newBlock] = await once(bot, 'blockUpdate:' + new Vec3(1, 2, 3), {array: true}) + expect(newBlock.type).toEqual(95) + }) + test('can use /xp', async () => { + bot.chat('/xp 100') + await once(bot, 'experience') + expect(bot.experience.points).toEqual(100) + }) + }) +}) diff --git a/test/portal.test.js b/test/portal.test.js new file mode 100644 index 0000000..982d7da --- /dev/null +++ b/test/portal.test.js @@ -0,0 +1,253 @@ +const { + portal_detector: { + detectFrame, + findPotentialLines, + findBorder, + getAir, + generateLine, + generatePortal, + makeWorldWithPortal + } +} = require('flying-squid') + +const { Vec3 } = require('vec3') +const { range } = require('range') + +describe('generate portal', () => { + test('generate a line', () => { + expect(generateLine(new Vec3(3, 1, 1), new Vec3(1, 0, 0), 2)).toEqual([new Vec3(3, 1, 1), new Vec3(4, 1, 1)]) + }) + + test('generate a portal', () => { + expect(generatePortal(new Vec3(2, 1, 1), new Vec3(1, 0, 0), 4, 5)).toEqual({ + bottom: generateLine(new Vec3(3, 1, 1), new Vec3(1, 0, 0), 2), + left: generateLine(new Vec3(2, 2, 1), new Vec3(0, 1, 0), 3), + right: generateLine(new Vec3(5, 2, 1), new Vec3(0, 1, 0), 3), + top: generateLine(new Vec3(3, 5, 1), new Vec3(1, 0, 0), 2), + air: generateLine(new Vec3(3, 2, 1), new Vec3(0, 1, 0), 3).concat(generateLine(new Vec3(4, 2, 1), new Vec3(0, 1, 0), 3)) + }) + }) +}) + +describe('detect portal', () => { + jest.setTimeout(60 * 1000) + const portalData = [] + + portalData.push({ + name: 'simple portal frame x', + bottomLeft: new Vec3(2, 1, 1), + direction: new Vec3(1, 0, 0), + width: 4, + height: 5, + additionalAir: [], + additionalObsidian: [] + }) + + portalData.push({ + name: 'simple portal frame z', + bottomLeft: new Vec3(2, 1, 1), + direction: new Vec3(0, 0, 1), + width: 4, + height: 5, + additionalAir: [], + additionalObsidian: [] + }) + + portalData.push({ + name: 'big simple portal frame x', + bottomLeft: new Vec3(2, 1, 1), + direction: new Vec3(1, 0, 0), + width: 10, + height: 10, + additionalAir: [], + additionalObsidian: [] + }) + + portalData.push({ + name: 'simple portal frame x with borders', + bottomLeft: new Vec3(2, 1, 1), + direction: new Vec3(1, 0, 0), + width: 4, + height: 5, + additionalAir: [], + additionalObsidian: [new Vec3(2, 1, 1), new Vec3(5, 1, 1), new Vec3(2, 6, 1), new Vec3(5, 6, 1)] + }) + + const {bottom, left, right, top, air} = generatePortal(new Vec3(2, 1, 2), new Vec3(1, 0, 0), 4, 5) + + portalData.push({ + name: '2 portals', + bottomLeft: new Vec3(2, 1, 1), + direction: new Vec3(1, 0, 0), + width: 4, + height: 5, + additionalAir: air, + additionalObsidian: [].concat.apply([], [bottom, left, right, top]) + }) + + portalData.push({ + name: 'huge simple portal frame z', + bottomLeft: new Vec3(2, 1, 1), + direction: new Vec3(0, 0, 1), + width: 50, + height: 50, + additionalAir: [], + additionalObsidian: [] + }) + + portalData.forEach(({name, bottomLeft, direction, width, height, additionalAir, additionalObsidian}) => { + const portal = generatePortal(bottomLeft, direction, width, height) + const {bottom, left, right, top, air} = portal + describe('Detect ' + name, () => { + const expectedBorder = {bottom, left, right, top} + + let world + beforeAll(async function () { + world = await makeWorldWithPortal(portal, additionalAir, additionalObsidian) + }) + + describe('detect potential first lines', () => { + test('detect potential first lines from bottom left', async () => { + let potentialLines = await findPotentialLines(world, bottom[0], new Vec3(0, 1, 0)) + expect(potentialLines).toContainEqual({ + 'direction': direction, + 'line': bottom + }) + }) + + test('detect potential first lines from bottom right', async () => { + let potentialLines = await findPotentialLines(world, bottom[bottom.length - 1], new Vec3(0, 1, 0)) + expect(potentialLines).toContainEqual({ + 'direction': direction, + 'line': bottom + }) + }) + + test('detect potential first lines from top left', async () => { + let potentialLines = await findPotentialLines(world, top[0], new Vec3(0, -1, 0)) + expect(potentialLines).toContainEqual({ + 'direction': direction, + 'line': top + }) + }) + + test('detect potential first lines from top right', async () => { + let potentialLines = await findPotentialLines(world, top[top.length - 1], new Vec3(0, -1, 0)) + expect(potentialLines).toContainEqual({ + 'direction': direction, + 'line': top + }) + }) + + test('detect potential first lines from left top', async () => { + let potentialLines = await findPotentialLines(world, left[left.length - 1], direction) + expect(potentialLines).toEqual([{ + 'direction': new Vec3(0, 1, 0), + 'line': left + }]) + }) + + test('detect potential first lines from right bottom', async () => { + let potentialLines = await findPotentialLines(world, right[0], direction.scaled(-1)) + expect(potentialLines).toEqual([{ + 'direction': new Vec3(0, 1, 0), + 'line': right + }]) + }) + }) + + describe('find borders', () => { + test('find borders from bottom', async () => { + const border = await findBorder(world, { + 'direction': direction, + 'line': bottom + }, new Vec3(0, 1, 0)) + expect(border).toEqual(expectedBorder) + }) + + test('find borders from top', async () => { + const border = await findBorder(world, { + 'direction': direction, + 'line': top + }, new Vec3(0, -1, 0)) + expect(border).toEqual(expectedBorder) + }) + + test('find borders from left', async () => { + const border = await findBorder(world, { + 'direction': new Vec3(0, 1, 0), + 'line': left + }, direction) + expect(border).toEqual(expectedBorder) + }) + test('find borders from right', async () => { + const border = await findBorder(world, { + 'direction': new Vec3(0, 1, 0), + 'line': right + }, direction.scaled(-1)) + expect(border).toEqual(expectedBorder) + }) + }) + + describe('detect portals', () => { + test('detect portals from bottom left', async () => { + const portals = await detectFrame(world, bottom[0], new Vec3(0, 1, 0)) + expect(portals).toEqual([portal]) + }) + test('detect portals from top left', async () => { + const portals = await detectFrame(world, top[0], new Vec3(0, -1, 0)) + expect(portals).toEqual([portal]) + }) + test('detect portals from right top', async () => { + const portals = await detectFrame(world, right[right.length - 1], direction.scaled(-1)) + expect(portals).toEqual([portal]) + }) + }) + + test('get air', () => { + const foundAir = getAir(expectedBorder) + expect(foundAir).toEqual(air) + }) + }) + }) +}) + +describe("doesn't detect non-portal", () => { + const portalData = [] + + portalData.push({ + name: 'simple portal frame x with one obsidian in the middle', + bottomLeft: new Vec3(2, 1, 1), + direction: new Vec3(1, 0, 0), + width: 5, + height: 5, + additionalAir: [], + additionalObsidian: [new Vec3(4, 3, 1)] + }) + + portalData.forEach(({name, bottomLeft, direction, width, height, additionalAir, additionalObsidian}) => { + const portal = generatePortal(bottomLeft, direction, width, height) + const {bottom, left, right, top} = portal + describe("doesn't detect detect " + name, () => { + let world + beforeAll(async function () { + world = await makeWorldWithPortal(portal, additionalAir, additionalObsidian) + }) + + describe("doesn't detect portals", () => { + test("doesn't detect portals from bottom left", async () => { + const portals = await detectFrame(world, bottom[0], new Vec3(0, 1, 0)) + expect(portals).toEqual([]) + }) + test("doesn't detect portals from top left", async () => { + const portals = await detectFrame(world, top[0], new Vec3(0, -1, 0)) + expect(portals).toEqual([]) + }) + test("doesn't detect portals from right top", async () => { + const portals = await detectFrame(world, right[right.length - 1], direction.scaled(-1)) + expect(portals).toEqual([]) + }) + }) + }) + }) +}) diff --git a/test/portal_detector.js b/test/portal_detector.js deleted file mode 100644 index 70be9ab..0000000 --- a/test/portal_detector.js +++ /dev/null @@ -1,247 +0,0 @@ -const {detectFrame,findPotentialLines,findBorder,getAir,generateLine,generatePortal,makeWorldWithPortal}=require("flying-squid").portal_detector; -const Vec3 = require("vec3").Vec3; -const assert = require('chai').assert; -const range = require('range').range; - - -describe("Generate portal",function(){ - it("generate a line",() => { - assert.deepEqual(generateLine(new Vec3(3,1,1),new Vec3(1,0,0),2),[new Vec3(3, 1, 1), new Vec3(4, 1, 1)]) - }); - it("generate a portal", () => { - assert.deepEqual(generatePortal(new Vec3(2,1,1),new Vec3(1,0,0),4,5),{ - bottom:generateLine(new Vec3(3,1,1),new Vec3(1,0,0),2), - left:generateLine(new Vec3(2,2,1),new Vec3(0,1,0),3), - right:generateLine(new Vec3(5,2,1),new Vec3(0,1,0),3), - top:generateLine(new Vec3(3,5,1),new Vec3(1,0,0),2), - air:generateLine(new Vec3(3,2,1),new Vec3(0,1,0),3).concat(generateLine(new Vec3(4,2,1),new Vec3(0,1,0),3)) - }) - }); -}); - -describe("Detect portal", function() { - this.timeout(60 * 1000); - const portalData=[]; - portalData.push({ - name:"simple portal frame x", - bottomLeft:new Vec3(2,1,1), - direction:new Vec3(1,0,0), - width:4, - height:5, - additionalAir:[], - additionalObsidian:[] - }); - portalData.push({ - name:"simple portal frame z", - bottomLeft:new Vec3(2,1,1), - direction:new Vec3(0,0,1), - width:4, - height:5, - additionalAir:[], - additionalObsidian:[] - }); - portalData.push({ - name:"big simple portal frame x", - bottomLeft:new Vec3(2,1,1), - direction:new Vec3(1,0,0), - width:10, - height:10, - additionalAir:[], - additionalObsidian:[] - }); - portalData.push({ - name:"simple portal frame x with borders", - bottomLeft:new Vec3(2,1,1), - direction:new Vec3(1,0,0), - width:4, - height:5, - additionalAir:[], - additionalObsidian:[new Vec3(2,1,1),new Vec3(5,1,1),new Vec3(2,6,1),new Vec3(5,6,1)] - }); - const {bottom,left,right,top,air}=generatePortal(new Vec3(2,1,2),new Vec3(1,0,0),4,5); - - portalData.push({ - name:"2 portals", - bottomLeft:new Vec3(2,1,1), - direction:new Vec3(1,0,0), - width:4, - height:5, - additionalAir:air, - additionalObsidian:[].concat.apply([], [bottom, left, right,top]) - }); - - - portalData.push({ - name:"huge simple portal frame z", - bottomLeft:new Vec3(2,1,1), - direction:new Vec3(0,0,1), - width:50, - height:50, - additionalAir:[], - additionalObsidian:[] - }); - - - portalData.forEach(({name,bottomLeft,direction,width,height,additionalAir,additionalObsidian}) => { - const portal=generatePortal(bottomLeft,direction,width,height); - const {bottom,left,right,top,air}=portal; - describe("Detect "+name,() => { - const expectedBorder={bottom,left,right,top}; - - let world; - before(async function(){ - world=await makeWorldWithPortal(portal,additionalAir,additionalObsidian); - }); - - - describe("detect potential first lines",function(){ - it("detect potential first lines from bottom left", async function() { - let potentialLines=await findPotentialLines(world,bottom[0],new Vec3(0,1,0)); - assert.include(potentialLines,{ - "direction": direction, - "line": bottom - }); - }); - - it("detect potential first lines from bottom right", async function() { - let potentialLines=await findPotentialLines(world,bottom[bottom.length-1],new Vec3(0,1,0)); - assert.include(potentialLines,{ - "direction": direction, - "line": bottom - }); - }); - - - it("detect potential first lines from top left", async function() { - let potentialLines=await findPotentialLines(world,top[0],new Vec3(0,-1,0)); - assert.include(potentialLines,{ - "direction": direction, - "line": top - }); - }); - - it("detect potential first lines from top right", async function() { - let potentialLines=await findPotentialLines(world,top[top.length-1],new Vec3(0,-1,0)); - assert.include(potentialLines,{ - "direction": direction, - "line": top - }); - }); - - it("detect potential first lines from left top", async function() { - let potentialLines=await findPotentialLines(world,left[left.length-1],direction); - assert.include(potentialLines,{ - "direction": new Vec3(0,1,0), - "line": left - }); - }); - - it("detect potential first lines from right bottom", async function() { - let potentialLines=await findPotentialLines(world,right[0],direction.scaled(-1)); - assert.include(potentialLines,{ - "direction": new Vec3(0,1,0), - "line": right - }); - }); - }); - - - describe("find borders",function() { - it("find borders from bottom", async function () { - const border = await findBorder(world, { - "direction": direction, - "line": bottom - }, new Vec3(0, 1, 0)); - assert.deepEqual(border, expectedBorder) - }); - - it("find borders from top", async function () { - const border = await findBorder(world, { - "direction": direction, - "line": top - }, new Vec3(0, -1, 0)); - assert.deepEqual(border, expectedBorder) - }); - - it("find borders from left", async function () { - const border = await findBorder(world, { - "direction": new Vec3(0, 1, 0), - "line": left - },direction); - assert.deepEqual(border, expectedBorder) - }); - it("find borders from right", async function () { - const border = await findBorder(world, { - "direction": new Vec3(0, 1, 0), - "line": right - }, direction.scaled(-1)); - assert.deepEqual(border, expectedBorder) - }); - }); - - describe("detect portals",function(){ - it("detect portals from bottom left",async function() { - const portals=await detectFrame(world,bottom[0],new Vec3(0,1,0)); - assert.deepEqual(portals,[portal]) - }); - it("detect portals from top left",async function() { - const portals=await detectFrame(world,top[0],new Vec3(0,-1,0)); - assert.deepEqual(portals,[portal]) - }); - it("detect portals from right top",async function() { - const portals=await detectFrame(world,right[right.length-1],direction.scaled(-1)); - assert.deepEqual(portals,[portal]) - }) - }); - - it("get air",function(){ - const foundAir=getAir(expectedBorder); - assert.deepEqual(foundAir,air); - }); - }); - }); - - -}); - - -describe("Doesn't detect non-portal",function() { - const portalData=[]; - - portalData.push({ - name:"simple portal frame x with one obsidian in the middle", - bottomLeft:new Vec3(2,1,1), - direction:new Vec3(1,0,0), - width:5, - height:5, - additionalAir:[], - additionalObsidian:[new Vec3(4,3,1)] - }); - - portalData.forEach(({name,bottomLeft,direction,width,height,additionalAir,additionalObsidian}) => { - const portal = generatePortal(bottomLeft, direction, width, height); - const {bottom,left,right,top}=portal; - describe("Doesn't detect detect " + name, () => { - let world; - before(async function () { - world=await makeWorldWithPortal(portal, additionalAir, additionalObsidian); - }); - - describe("doesn't detect portals",function(){ - it("doesn't detect portals from bottom left",async function() { - const portals=await detectFrame(world,bottom[0],new Vec3(0,1,0)); - assert.deepEqual(portals,[]) - }); - it("doesn't detect portals from top left",async function() { - const portals=await detectFrame(world,top[0],new Vec3(0,-1,0)); - assert.deepEqual(portals,[]) - }); - it("doesn't detect portals from right top",async function() { - const portals=await detectFrame(world,right[right.length-1],direction.scaled(-1)); - assert.deepEqual(portals,[]) - }) - }); - - }); - }); -}); diff --git a/test/simple.js b/test/simple.js deleted file mode 100644 index 5100d84..0000000 --- a/test/simple.js +++ /dev/null @@ -1,22 +0,0 @@ -const net = require('net'); -describe("Server", function() { - let serv; - before(function(done){ - serv=require("../app"); - serv.on("listening",function(){ - done(null); - }) - }); - - after(function(done){ - serv._server.close(); - serv._server.on("close",function(){ - done(); - }); - }); - it("Is running", function(done) { - const client = net.Socket(); - client.connect(serv._server.socketServer.address().port, '127.0.0.1', done); - client.on('error', done); - }); -}); diff --git a/test/simple.test.js b/test/simple.test.js new file mode 100644 index 0000000..89adba0 --- /dev/null +++ b/test/simple.test.js @@ -0,0 +1,35 @@ +const net = require('net') +const squid = require('flying-squid') + +const settings = require('../config/default-settings') + +describe('server', () => { + let serv + + beforeAll(done => { + const options = settings + options['online-mode'] = false + options['port'] = 25566 + options['view-distance'] = 2 + options['worldFolder'] = undefined + options['logging'] = false + serv = squid.createMCServer(options) + + serv.on('listening', () => { + done() + }) + }) + + afterAll(done => { + serv._server.close() + serv._server.on('close', () => { + done() + }) + }) + + test('is running', done => { + const client = net.Socket() + client.connect(serv._server.socketServer.address().port, '127.0.0.1', done) + client.on('error', done) + }) +})