_handleMessage(header, bodyBuf, conn, multiId) {
		// Is this a multi? If yes, short-circuit and just process it now.
		if (header.msg == EMsg.Multi) {
			this._processMulti(header, SteamUserMessages._decodeProto(protobufs[EMsg.Multi], bodyBuf), conn);
			return;
		}

		let msgName = EMsg[header.msg] || header.msg;
		let handlerName = header.msg;

		let debugPrefix = multiId ? `[${multiId}] ` : (conn ? `[${conn.connectionType[0]}${conn.connectionId}] ` : '');

		let isServiceMethodMsg = [EMsg.ServiceMethod, EMsg.ServiceMethodResponse].includes(header.msg);
		if (isServiceMethodMsg) {
			if (header.proto && header.proto.target_job_name) {
				handlerName = msgName = header.proto.target_job_name;
				if (header.msg == EMsg.ServiceMethodResponse) {
					handlerName += '_Response';
					msgName += '_Response';
				}
			} else {
				this.emit('debug', debugPrefix + 'Got ' + (header.msg == EMsg.ServiceMethod ? 'ServiceMethod' : 'ServiceMethodResponse') + ' without target_job_name');
				return;
			}
		}

		if (!isServiceMethodMsg && header.proto && header.proto.target_job_name) {
			this.emit('debug', debugPrefix + 'Got unknown target_job_name ' + header.proto.target_job_name + ' for msg ' + msgName);
		}

		if (!this._handlerManager.hasHandler(handlerName) && this._jobs.get(header.targetJobID.toString()) === null) {
			this.emit(VERBOSE_EMSG_LIST.includes(header.msg) ? 'debug-verbose' : 'debug', debugPrefix + 'Unhandled message: ' + msgName);
			return;
		}

		let body = bodyBuf;
		if (protobufs[handlerName]) {
			body = SteamUserMessages._decodeProto(protobufs[handlerName], bodyBuf);
		}

		let handledMessageDebugMsg = debugPrefix + 'Handled message: ' + msgName;
		if (header.msg == EMsg.ClientLogOnResponse) {
			handledMessageDebugMsg += ` (${EResult[body.eresult] || body.eresult})`;
		}
		this.emit(VERBOSE_EMSG_LIST.includes(header.msg) ? 'debug-verbose' : 'debug', handledMessageDebugMsg);

		let cb = null;
		if (header.sourceJobID != JOBID_NONE) {
			// this message expects a response. make a callback we can pass to the end-user.
			cb = (emsgOrHeader, body) => {
				// once invoked the callback should set the jobid_target
				let responseHeader = typeof emsgOrHeader === 'object' ? emsgOrHeader : {msg: emsgOrHeader};
				let emsg = responseHeader.msg;

				if (protobufs[emsg]) {
					responseHeader.proto = {jobid_target: header.sourceJobID};
				} else {
					responseHeader.targetJobID = header.sourceJobID;
				}

				this._send(responseHeader, body);
			};
		}

		let jobCallback = this._jobs.get(header.targetJobID.toString());
		if (jobCallback) {
			// this is a response to something, so invoke the appropriate callback
			jobCallback.call(this, body, header, cb);
		} else {
			this._handlerManager.emit(this, handlerName, body, header, cb);
		}
	}
Handles and routes a parsed message
@param {object} header
@param {ByteBuffer} bodyBuf
@param {BaseConnection} [conn]
@param {string} [multiId]
@protected