func (m *AreaOfInterestMap) Insert(connID uint16, userID []byte, x float64, y float64) {
	var cellX, cellY = uint16(x / m.cellSizeW), uint16(y / m.cellSizeH)
	if cellX >= m.cellNumW || cellY >= m.cellNumH {
		return
	}
	var cell = m.cellMatrix[cellX][cellY]
	var newElem = &realtimeAOICell{
		connID:  connID,
		userID:  userID,
		offsetX: math.Mod(x, m.cellSizeW),
		offsetY: math.Mod(y, m.cellSizeH),
		pred:    nil,
		next:    nil,
		aoiCell: cell,
	}
	if cell.list == nil {
		cell.list = newElem
	} else {
		var elem = cell.list
		for elem.next != nil {
			elem = elem.next
		}
		elem.next = newElem
		newElem.pred = elem
	}
	m.mapByConnID[connID] = newElem
	m.broadcastAdjacency(cell, m.insertPacketFn(connID, userID, x, y))
}
Insert inserts a user into area of interest map
package realtime
import (
	"math"
)
type realtimeAOICell struct {
	connID           uint16
	userID           []byte
	offsetX, offsetY float64
	pred, next       *realtimeAOICell
	aoiCell *aoiCell
}
func (e *realtimeAOICell) absolutePosition() (x float64, y float64) {
	x = e.offsetX + float64(e.aoiCell.x)*e.aoiCell.aoiMap.cellSizeW
	y = e.offsetY + float64(e.aoiCell.y)*e.aoiCell.aoiMap.cellSizeH
	return
}
type aoiCell struct {
	x, y uint16
	list *realtimeAOICell
	aoiMap *AreaOfInterestMap
}
type AreaOfInterestMap struct {
	mapID                uint16
	cellMatrix           [][]*aoiCell
	cellNumW, cellNumH   uint16
	cellSizeW, cellSizeH float64
	mapByConnID map[uint16]*realtimeAOICell
	sendMessageFn  func(targets []uint16, resp Response)
	insertPacketFn func(connID uint16, userID []byte, x float64, y float64) (resp Response)
	removePacketFn func(connID uint16, userID []byte) (resp Response)
	movePacketFn   func(connID uint16, userID []byte, x float64, y float64, speed float64) (resp Response)
}
func (m *AreaOfInterestMap) OnInsertUser(
	fn func(connID uint16, userID []byte, x float64, y float64) (resp Response)) (ret *AreaOfInterestMap) {
	m.insertPacketFn = fn
	return m
}
func (m *AreaOfInterestMap) OnRemoveUser(fn func(connID uint16, userID []byte) (resp Response)) (ret *AreaOfInterestMap) {
	m.removePacketFn = fn
	return m
}
func (m *AreaOfInterestMap) OnMoveUser(
	fn func(connID uint16, userID []byte, x float64, y float64, speed float64) (resp Response)) (ret *AreaOfInterestMap) {
	m.movePacketFn = fn
	return m
}
func NewAreaOfInterestMap(
	server Server,
	mapID uint16,
	sizeX float64, sizeY float64,
	cellNumW uint16, cellNumH uint16) (newMap *AreaOfInterestMap) {
	newMap = &AreaOfInterestMap{
		mapID:       mapID,
		cellMatrix:  make([][]*aoiCell, cellNumH),
		cellNumW:    cellNumW,
		cellNumH:    cellNumH,
		cellSizeW:   sizeX / float64(cellNumW),
		cellSizeH:   sizeY / float64(cellNumH),
		mapByConnID: make(map[uint16]*realtimeAOICell),
		sendMessageFn: func(targets []uint16, resp Response) {
			server.SendMessageMultiple(targets, resp)
		},
		insertPacketFn: func(connID uint16, userID []byte, x float64, y float64) (resp Response) {
			resp = server.(*serverImpl).responsePool.Get().(Response)
			defer server.(*serverImpl).responsePool.Put(resp)
			resp.Reset()
			resp.SetEventCode(EventCodeUserEnteredAOIMap)
			resp.WriteUint16(newMap.mapID).WriteUint16(connID).WriteFloat64(x).WriteFloat64(y)
			return resp
		},
		removePacketFn: func(connID uint16, userID []byte) (resp Response) {
			resp = server.(*serverImpl).responsePool.Get().(Response)
			defer server.(*serverImpl).responsePool.Put(resp)
			resp.Reset()
			resp.SetEventCode(EventCodeUserExitedAOIMap)
			resp.WriteUint16(newMap.mapID).WriteUint16(connID)
			return resp
		},
		movePacketFn: func(connID uint16, userID []byte, x float64, y float64, speed float64) (resp Response) {
			resp = server.(*serverImpl).responsePool.Get().(Response)
			defer server.(*serverImpl).responsePool.Put(resp)
			resp.Reset()
			resp.SetEventCode(EventCodeUserMovedOnAOIMap)
			resp.WriteUint16(newMap.mapID).WriteUint16(connID)
			resp.WriteFloat64(x).WriteFloat64(y).WriteFloat64(speed)
			return resp
		},
	}
	for r := 0; r < len(newMap.cellMatrix); r++ {
		newMap.cellMatrix[r] = make([]*aoiCell, cellNumW)
		for c := 0; c < len(newMap.cellMatrix[r]); c++ {
			newMap.cellMatrix[r][c] = &aoiCell{
				x:      uint16(r),
				y:      uint16(c),
				list:   nil,
				aoiMap: newMap,
			}
		}
	}
	return newMap
}
func (m *AreaOfInterestMap) broadcastAdjacency(centralCell *aoiCell, resp Response) {
	var sendTarget = make([]uint16, 0)
	var elem = centralCell.list
	for elem != nil {
		sendTarget = append(sendTarget, elem.connID)
		elem = elem.next
	}
	if centralCell.x > 0 {
		elem = m.cellMatrix[centralCell.x-1][centralCell.y].list
		for elem != nil {
			sendTarget = append(sendTarget, elem.connID)
			elem = elem.next
		}
	}
	if centralCell.x < m.cellNumW-1 {
		elem = m.cellMatrix[centralCell.x+1][centralCell.y].list
		for elem != nil {
			sendTarget = append(sendTarget, elem.connID)
			elem = elem.next
		}
	}
	if centralCell.y > 0 {
		if centralCell.x > 0 {
			elem = m.cellMatrix[centralCell.x-1][centralCell.y-1].list
			for elem != nil {
				sendTarget = append(sendTarget, elem.connID)
				elem = elem.next
			}
		}
		elem = m.cellMatrix[centralCell.x][centralCell.y-1].list
		for elem != nil {
			sendTarget = append(sendTarget, elem.connID)
			elem = elem.next
		}
		if centralCell.x < m.cellNumW-1 {
			elem = m.cellMatrix[centralCell.x+1][centralCell.y-1].list
			for elem != nil {
				sendTarget = append(sendTarget, elem.connID)
				elem = elem.next
			}
		}
	}
	if centralCell.y < m.cellNumH-1 {
		if centralCell.x > 0 {
			elem = m.cellMatrix[centralCell.x-1][centralCell.y+1].list
			for elem != nil {
				sendTarget = append(sendTarget, elem.connID)
				elem = elem.next
			}
		}
		elem = m.cellMatrix[centralCell.x][centralCell.y+1].list
		for elem != nil {
			sendTarget = append(sendTarget, elem.connID)
			elem = elem.next
		}
		if centralCell.x < m.cellNumW-1 {
			elem = m.cellMatrix[centralCell.x+1][centralCell.y+1].list
			for elem != nil {
				sendTarget = append(sendTarget, elem.connID)
				elem = elem.next
			}
		}
	}
	m.sendMessageFn(sendTarget, resp)
}