universo-platform-3d
270 строк · 6.7 Кб
1import { InternalServerErrorException } from '@nestjs/common'
2import { IBasis } from '../godot-types/basis.model'
3import { ITransform } from '../godot-types/transform.model'
4import { IVector3 } from '../godot-types/vector3.model'
5import {
6getGodotPaddingByType,
7GodotTypes
8} from './serialization/godot.mapping'
9
10export default class ArrayReadStream {
11private index: number
12private dataView: DataView
13private buffer: Buffer
14
15constructor(buffer: Buffer) {
16this.buffer = buffer
17
18const uint8Array = new Uint8Array(buffer.length)
19for (let counter = 0; counter < buffer.length; counter++) {
20uint8Array[counter] = buffer[counter]
21}
22this.dataView = new DataView(uint8Array.buffer, 0, uint8Array.length)
23this.index = 0
24
25this.initBuffer()
26}
27
28private initBuffer() {
29this.readArray()
30}
31
32public getLength(): number {
33const lengthIndex = getGodotPaddingByType(GodotTypes.ARRAY) // Skip Array padding
34return this.dataView.getInt32(lengthIndex, true)
35}
36
37public getEventCode(): number {
38const eventCodeIndex =
39getGodotPaddingByType(GodotTypes.ARRAY) + // Skip Array padding
40getGodotPaddingByType(GodotTypes.INT) + // Skip Array length
41getGodotPaddingByType(GodotTypes.INT) // Skip Event code type
42return this.dataView.getInt32(eventCodeIndex, true)
43}
44
45public validateType(expectedType: GodotTypes) {
46const type = this.dataView.getInt32(this.index, true)
47if (type !== expectedType) {
48throw new InternalServerErrorException(
49`Data type ${type} does not match expected data type ${expectedType}`
50)
51}
52
53const padding = getGodotPaddingByType(type)
54this.index += padding
55}
56
57public readInt(validate = true): number {
58if (validate) {
59this.validateType(GodotTypes.INT)
60}
61if (this.dataView.byteLength > this.index) {
62const value = this.dataView.getInt32(this.index, true)
63this.index += 4
64return value
65} else {
66throw new InternalServerErrorException(
67'Could not read value of type "number"'
68)
69}
70}
71
72public readFloat(validate = true): number {
73if (validate) {
74this.validateType(GodotTypes.FLOAT)
75}
76if (this.dataView.byteLength > this.index) {
77const value = this.dataView.getFloat32(this.index, true)
78this.index += 4
79return value
80} else {
81throw new InternalServerErrorException(
82'Could not read value of type "float"'
83)
84}
85}
86
87private readStringLength(): number {
88const length = this.dataView.getInt32(this.index, true)
89this.index += 4
90return length
91}
92
93public readString(validate = true): string {
94if (validate) {
95this.validateType(GodotTypes.STRING)
96}
97const length = this.readStringLength()
98
99const textDecoder = new TextDecoder('ascii')
100const slice = this.dataView.buffer.slice(this.index, this.index + length)
101this.dataView.byteOffset
102const str = textDecoder.decode(slice)
103if (/[\u0080-\uffff]/.test(str)) {
104throw new Error('this string seems to contain (still encoded) multibytes')
105}
106this.index += Math.ceil(length / 4) * 4
107return str
108}
109
110public readVector3(validate = true): IVector3 {
111if (validate) {
112this.validateType(GodotTypes.VECTOR3)
113}
114const x = this.readFloat(false)
115const y = this.readFloat(false)
116const z = this.readFloat(false)
117return {
118x,
119y,
120z
121}
122}
123
124public readBasis(validate = true): IBasis {
125if (validate) {
126this.validateType(GodotTypes.BASIS)
127}
128const x1 = this.readFloat(false)
129const y1 = this.readFloat(false)
130const z1 = this.readFloat(false)
131const x2 = this.readFloat(false)
132const y2 = this.readFloat(false)
133const z2 = this.readFloat(false)
134const x3 = this.readFloat(false)
135const y3 = this.readFloat(false)
136const z3 = this.readFloat(false)
137return {
138x1,
139y1,
140z1,
141x2,
142y2,
143z2,
144x3,
145y3,
146z3
147}
148}
149
150public readBool(validate = true) {
151if (validate) {
152this.validateType(GodotTypes.BOOL)
153}
154
155const num = this.readInt(false)
156const bool = Boolean(num)
157return bool
158}
159
160public readPoolByteArray(): Uint8Array {
161this.validateType(GodotTypes.POOL_BYTE_ARRAY)
162let length = this.readInt(false)
163length = (length / 4) * 4
164const uint8Array = new Uint8Array(length)
165for (let i = 0; i < length; i++) {
166const byte = this.dataView.getUint8(this.index + i)
167uint8Array[i] = byte
168}
169this.index += length
170return uint8Array
171}
172
173public readTransform(validate = true) {
174if (validate) {
175this.validateType(GodotTypes.TRANSFORM)
176}
177const basis = this.readBasis(false)
178const origin = this.readVector3(false)
179
180return {
181basis,
182origin
183}
184}
185
186public readDictionary<K, T>(keyFunc: () => K, valueFunc: () => T) {
187this.validateType(GodotTypes.DICTIONARY)
188const length = this.readInt(false)
189
190const boundKeyFunc = keyFunc.bind(this)
191const boundValueFunc = valueFunc.bind(this)
192
193const dictionary = new Map<string, ITransform>()
194for (let i = 0; i < length; i++) {
195const key = boundKeyFunc()
196const transform = boundValueFunc()
197dictionary.set(key, transform)
198}
199
200return dictionary
201}
202
203// Validates array start
204// Returns length
205public readArray(validate = true): number {
206if (validate) {
207this.validateType(GodotTypes.ARRAY)
208}
209const length = this.readInt(false)
210return length
211}
212
213public readAll(): any[] {
214const array: any[] = []
215try {
216while (this.hasNext()) {
217const value = this.readNext()
218if (value) {
219array.push(value)
220}
221}
222} catch {
223return array
224}
225}
226
227public readNext(): any | any[] {
228const type = this.readInt(false)
229let value: any | any[]
230switch (type) {
231case GodotTypes.INT:
232value = this.readInt(false)
233break
234case GodotTypes.ARRAY:
235const arrayLength = this.readArray(false)
236value = []
237for (let i = 0; i < arrayLength; i++) {
238value = this.readNext()
239}
240break
241case GodotTypes.BOOL:
242value = this.readBool(false)
243break
244case GodotTypes.STRING:
245value = this.readString(false)
246break
247case GodotTypes.VECTOR3:
248value = this.readVector3(false)
249break
250case GodotTypes.TRANSFORM:
251value = this.readTransform(false)
252break
253case GodotTypes.FLOAT:
254value = this.readFloat(false)
255break
256case GodotTypes.BASIS:
257value = this.readBasis(false)
258break
259}
260return value
261}
262
263public hasNext(): boolean {
264return this.index <= this.buffer.length
265}
266
267public getBuffer(): Buffer {
268return this.buffer
269}
270}
271