UNPKG

@magikcraft/mct1

Version:

Minecraft for Type 1 Diabetes

405 lines (356 loc) 12.7 kB
import * as events from 'events' import { Logger } from '@magikcraft/mct1/log' import * as tools from '@magikcraft/mct1/tools' import { worldDelete } from '@magikcraft/mct1/world/index' import * as server from '@magikcraft/mct1/utils/server' const Biome = Java.type('org.bukkit.block.Biome') const log = Logger(__filename) interface IWorldRegion { name: string loc1: any // region loc2: any enterEventHandlers: IWorldRegionEventHandler[] exitEventHandlers: IWorldRegionEventHandler[] } interface IWorldRegionEventHandler { handler: any player?: any } interface IWorldPlayer { player: any moveCount: number inRegionNames: string[] } // User class export default class World { private world private started: boolean = false private regions: IWorldRegion[] = [] private regionEvents private worldPlayers: IWorldPlayer[] = [] private logger private events: any = {} private intervals: any = {} private timers: any = {} private destroyWorldIfEmpty: boolean = false private destroyWorldIfEmptyDelay: number = 3000 constructor(world) { this.world = world this.logger = Logger(`world--${this.world.name}`) this._watchPlayersJoinWorld() } start() { this._watchPlayersMove() this._watchPlayersLeaveWorld() this.started = true } stop() { this.unregisterAllEvents() this.clearAllTimeouts() this.clearAllIntervals() // Restart watcher this._watchPlayersJoinWorld() this.started = false } cleanse() { this.unregisterAllEvents() this.clearAllTimeouts() this.clearAllIntervals() } // setTime = (time: 'dawn' | 'day' | 'dusk' | 'night') => server.executeCommand(`time ${time} ${this.world.name}`) setDawn = () => this.world.setTime(6000) setDay = () => this.world.setTime(12000) setDusk = () => this.world.setTime(18000) setNight = () => this.world.setTime(20000) setSun = () => this.world.setStorm(false) || this.world.setThundering(false) setStorm = () => this.world.setThundering(true) || this.world.setStorm(true) setRain = () => this.world.setStorm(true) setChunkBiome(loc, biome: string) { const chunk = this.world.getChunkAt(loc) for (let x = 0; x < 16; x++) { for (let z = 0; z < 16; z++) { const block = chunk.getBlock(x, 0, z) this.log('setChunkBiome', `${x} 0 ${z}`) this.log('block.getBiome()', block.getBiome()) block.setBiome(Biome[biome]) } } } registerRegion(regionName: string, loc1, loc2) { this.regions.push({ name: regionName, loc1: loc1, loc2: loc2, enterEventHandlers: [], exitEventHandlers: [], }) } log(label: string, log?: any) { this.logger(label, log) } registerPlayerEnterRegionEvent = (regionName, handler, player?: any) => { this._registerPlayerRegionEvent('enter', regionName, handler, player) } registerPlayerExitRegionEvent(regionName, handler, player?: any) { this._registerPlayerRegionEvent('exit', regionName, handler, player) } preventDeadPlayerDrops() { this.registerEvent('playerDeath', event => { if (event.entity.type != 'PLAYER') return // Clean-up dropped items setTimeout(() => { event.entity.getNearbyEntities(1, 1, 1).forEach(entity => { if (entity.type == 'DROPPED_ITEM' && entity.name) { entity.remove() } }) }, 1) }) } preventBlockBreak(except: string[] = []) { this.registerEvent('blockBreak', event => { if (event.block.world.name !== this.world.name) return const blockType = event.block.type.toString() if (except.includes(blockType)) return event.setCancelled(true) }) } allowMobSpawning = () => { this.unregisterEvent('preventMobSpawning') } preventMobSpawning(except: string[] = []) { this.unregisterEvent('preventMobSpawning') this.registerEvent( 'creatureSpawn', event => { if (event.entity.world.name !== this.world.name) return const mobType = event.entity.type.toString() if (except.includes(mobType)) return const isMonster = event.entity instanceof Java.type('org.bukkit.entity.Monster') if (!isMonster) return event.setCancelled(true) }, 'preventMobSpawning' ) } setDestroyWorldIfEmpty(bool: boolean, delay?: number) { this.destroyWorldIfEmpty = bool if (delay) { this.destroyWorldIfEmptyDelay = delay } } setTimeout(callback: any, interval: number, key?: string) { const k = key || tools.uuid() this.timers[k] = setTimeout(callback, interval) } clearTimeout(key: string) { clearTimeout(this.timers[key]) } clearTimeoutsLike(wildcard: string) { for (let key in this.timers) { if (key.includes(wildcard)) { this.clearTimeout(key) } } } clearAllTimeouts() { for (let key in this.timers) { this.clearTimeout(key) } } setInterval = function(callback: any, interval: number, key?: string) { const k = key || tools.uuid() this.intervals[k] = setInterval(callback, interval) } clearInterval(key: string) { clearInterval(this.intervals[key]) } clearIntervalsLike(wildcard: string) { for (let key in this.timers) { if (key.includes(wildcard)) { this.clearInterval(key) } } } clearAllIntervals() { for (let key in this.intervals) { this.clearInterval(key) } } registerEvent(type: string, callback: any, key?: string) { const k = key || tools.uuid() this.unregisterEvent(k) this.events[k] = events[type](callback) } unregisterEvent(key: string) { if (this.events[key]) this.events[key].unregister() } unregisterEventsLike(wildcard: string) { for (let key in this.events) { if (key.includes(wildcard)) { this.unregisterEvent(key) } } } unregisterAllEvents() { for (let key in this.events) { this.unregisterEvent(key) } } private _registerPlayerRegionEvent( type, regionName, handler, player?: any ) { let region this.regions.forEach(r => { // use forEach as find pollyfill may not be loaded... if (r.name === regionName) region = r }) if (region) { if (type === 'enter') region.enterEventHandlers.push({ handler, player }) if (type === 'exit') region.exitEventHandlers.push({ handler, player }) } } private _watchPlayersJoinWorld() { this.world.players.forEach(player => this._playerJoinedWorld(player)) this.registerEvent('playerJoin', event => { if (event.player.world.name !== this.world.name) return this._playerJoinedWorld(event.player) }) this.registerEvent('playerChangedWorld', event => { if (event.player.world.name !== this.world.name) return this._playerJoinedWorld(event.player) }) } private _playerJoinedWorld(player) { this.log(`player ${player.name} joined world ${this.world.name}`) const worldPlayer = { player: player, moveCount: 0, inRegionNames: [], } // Ensure clean // player.setBedSpawnLocation(this.world.getSpawnLocation()) this.worldPlayers.push(worldPlayer) this._playerMove(worldPlayer) // populate inRegions // One or more players in world. Run start. if (!this.started) { this.start() } } private _watchPlayersLeaveWorld() { this.registerEvent('playerChangedWorld', event => { if (event.from.name !== this.world.name) return this._playerLeftWorld(event.player) }) } private _playerLeftWorld(player) { this.log(`player ${player.name} left world ${this.world.name}`) this.worldPlayers = this.worldPlayers.filter( wp => wp.player.name != player.name ) // If no players are left in world. Run stop. if (!this.worldPlayers.length) { this.stop() if (this.destroyWorldIfEmpty) { this.setTimeout(() => { if (!this.worldPlayers.length) { worldDelete(this.world) server.executeCommand(`mv delete ${this.world.name}`) server.executeCommand(`mvconfirm`) } }, this.destroyWorldIfEmptyDelay) } } } private _watchPlayersMove() { this.registerEvent('playerMove', event => { if (event.player.world.name !== this.world.name) return if (!this.regions.length) return const worldPlayer = this.worldPlayers.find( p => event.player.name === p.player.name ) if (!worldPlayer) return worldPlayer.moveCount++ if (worldPlayer.moveCount % 3 !== 0) return this._playerMove(worldPlayer) }) } private _playerMove(worldPlayer) { // this.log('_playerMove') const player = worldPlayer.player // check if player exited any regions. worldPlayer.inRegionNames.forEach(regionName => { // this.log('exit check'); const region = this.regions.find(r => r.name === regionName) if (region) { if ( !this._regionContainsLocation( region, worldPlayer.player.location ) ) { // Remove from player.inRegionNames worldPlayer.inRegionNames = worldPlayer.inRegionNames.filter( name => region.name !== name ) // Log! log(`${player.name} exited region ${region.name}`) // Run handlers region.exitEventHandlers.forEach(handle => { if (handle.player && handle.player.name != player.name) return handle.handler({ player }) }) } } }) // check if player entered any regions. this.regions.forEach(region => { // this.log(`player ${player.name} enter check for region: ${region.name}`); const alreadyInRegion = worldPlayer.inRegionNames.find( rnanme => rnanme === region.name ) if ( !alreadyInRegion && this._regionContainsLocation(region, player.location) ) { // Add to player.inRegionNames worldPlayer.inRegionNames.push(region.name) // Log! this.log(`${player.name} entered region ${region.name}`) // Run handlers region.enterEventHandlers.forEach(handle => { if (handle.player && handle.player.name != player.name) return handle.handler({ player }) }) } }) } private _regionContainsLocation(reg: IWorldRegion, loc) { if ( (loc.x >= reg.loc1.x && loc.x <= reg.loc2.x) || (loc.x <= reg.loc1.x && loc.x >= reg.loc2.x) ) { if ( (loc.y >= reg.loc1.y && loc.y <= reg.loc2.y) || (loc.y <= reg.loc1.y && loc.y >= reg.loc2.y) ) { if ( (loc.z >= reg.loc1.z && loc.z <= reg.loc2.z) || (loc.z <= reg.loc1.z && loc.z >= reg.loc2.z) ) { return true } } } return false } }