From cbdf2af260f5b7183ea6b114ac989291fb13c650 Mon Sep 17 00:00:00 2001 From: cha0s Date: Fri, 12 Apr 2019 12:10:40 -0500 Subject: [PATCH] feat: parser throughput --- client/index.js | 32 ++++++++++++++++---- client/ui/index.js | 8 +++-- client/ui/throughput.js | 59 +++++++++++++++++++++++++++++++++++++ common/parser-throughput.js | 20 +++++++++++++ 4 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 client/ui/throughput.js create mode 100644 common/parser-throughput.js diff --git a/client/index.js b/client/index.js index 39d0fe0..a2e04e7 100644 --- a/client/index.js +++ b/client/index.js @@ -7,12 +7,14 @@ import {EntityList} from '@avocado/entity'; import {ActionRegistry} from '@avocado/input'; import {Stage} from '@avocado/graphics'; import {Vector} from '@avocado/math'; +import {SocketIoParser} from '@avocado/packet'; import {Synchronizer, Unpacker} from '@avocado/state'; import {Room, RoomView} from '@avocado/topdown'; import {World} from '@avocado/physics/matter/world'; import {clearAnimation, setAnimation, Ticker} from '@avocado/timing'; // 1st party. import {InputPacket, KeysPacket, StatePacket} from '../common/packet'; +import {augmentParserWithThroughput} from '../common/parser-throughput'; import {WorldTime} from '../common/world-time'; import Ui from './ui'; // DOM. @@ -38,11 +40,6 @@ stage.element.addEventListener('focus', () => { stage.addToDom(appNode); // World time ticker. const worldTimeTicker = new Ticker(1 / 10); -// UI. -const UiComponent = ; -ReactDOM.render(UiComponent, stage.ui); // Handle "own" entity. let selfEntity; function hasSelfEntity() { @@ -156,7 +153,22 @@ stage.on('pointerUp', (event) => { }); let actionState = actionRegistry.state; // Create the socket connection. -const socket = createClient(window.location.href); +const AugmentedParser = augmentParserWithThroughput(SocketIoParser); +const socket = createClient(window.location.href, { + parser: AugmentedParser, +}); +// Throughput ticker. +const throughputSampleTime = 0.5; +const throughputTicker = new Ticker(throughputSampleTime); +const {Decoder, Encoder} = AugmentedParser; +throughputTicker.on('tick', () => { + throughputTicker.emit('updateThroughput', Vector.scale([ + Decoder.throughput, + Encoder.throughput, + ], 1 / throughputSampleTime)); + Decoder.throughput = 0; + Encoder.throughput = 0; +}); // Input messages. const messageHandle = setInterval(() => { // Mouse/touch movement. @@ -199,6 +211,8 @@ const predictionHandle = setInterval(() => { renderTicker.tick(elapsed); // World time ticker. worldTimeTicker.tick(elapsed); + // Throughput ticker. + throughputTicker.tick(elapsed); // Apply environmental lighting. let intensity = 0; if (worldTime.hour >= 21 || worldTime.hour < 4) { @@ -256,3 +270,9 @@ function render() { dirty = false; } const renderHandle = setAnimation(render); +// UI. +const UiComponent = ; +ReactDOM.render(UiComponent, stage.ui); diff --git a/client/ui/index.js b/client/ui/index.js index 47200ff..2573978 100644 --- a/client/ui/index.js +++ b/client/ui/index.js @@ -2,10 +2,14 @@ import React from 'react'; import {hot} from 'react-hot-loader/root'; // 1st party. +import Throughput from './throughput'; import WorldTime from './world-time'; -const Ui = ({worldTimeTicker}) => { - return ; +const Ui = ({throughputTicker, worldTimeTicker}) => { + return + + + ; }; export default hot(Ui); diff --git a/client/ui/throughput.js b/client/ui/throughput.js new file mode 100644 index 0000000..63351ec --- /dev/null +++ b/client/ui/throughput.js @@ -0,0 +1,59 @@ +// 3rd party. +import React, {useEffect, useState} from 'react'; +// 2nd party. +import {compose} from '@avocado/core'; +import contempo from 'contempo'; +// 1st party. +import {WorldTime} from '../../common/world-time'; + +const decorate = compose( + contempo(` +.throughput { + background-color: rgba(0, 0, 0, .3); + color: white; + font-size: 0.6em; + position: absolute; + top: 0.5em; + left: 0.5em; + line-height: 1em; + width: 11em; + padding: 0.5em; + font-family: monospace; +} +.throughput .input:before { + content: 'kbps \\a0in: '; +} +.throughput .output:before { + content: 'kbps out: '; +} +.throughput p { + margin: 0.25em; +} + `), +); + +const formatKbps = (bytes) => { + const bps = bytes * 8; + const kbps = bps / 1000; + return Math.floor(kbps * 100) / 100; +} + +const ThroughputComponent = ({ticker}) => { + const [throughput, setThroughput] = useState([0, 0]); + useEffect(() => { + const updater = (throughput) => { + setThroughput(throughput); + }; + ticker.on('updateThroughput', updater); + return () => { + ticker.off('updateThroughput', updater); + }; + }, []); + return
+

{formatKbps(throughput[0])}

+

{formatKbps(throughput[1])}

+ +
; +} + +export default decorate(ThroughputComponent); diff --git a/common/parser-throughput.js b/common/parser-throughput.js new file mode 100644 index 0000000..2d2e4c9 --- /dev/null +++ b/common/parser-throughput.js @@ -0,0 +1,20 @@ + +export function augmentParserWithThroughput(Parser) { + const {Decoder, Encoder} = Parser; + // Decoder. + Decoder.throughput = 0; + const {parseBinary} = Decoder.prototype; + Decoder.prototype.parseBinary = function (obj) { + Decoder.throughput += obj.length || obj.byteLength; + return parseBinary.call(this, obj); + }; + // Encoder. + Encoder.throughput = 0; + const {pack} = Encoder.prototype; + Encoder.prototype.pack = function (packet) { + const buffer = pack.call(this, packet); + Encoder.throughput += buffer.length || buffer.byteLength; + return buffer; + }; + return Parser; +} \ No newline at end of file