@magikcraft/mct1
Version:
Minecraft for Type 1 Diabetes
405 lines (356 loc) • 12.7 kB
text/typescript
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
}
}