diff --git a/frontend/advanced.html b/frontend/advanced.html index b94c463..46a0995 100644 --- a/frontend/advanced.html +++ b/frontend/advanced.html @@ -67,6 +67,12 @@

Advanced

+
  • + + +

    Templates

    +
    +
  • diff --git a/frontend/app/controllers.js b/frontend/app/controllers.js index de9fcec..fd44749 100644 --- a/frontend/app/controllers.js +++ b/frontend/app/controllers.js @@ -88,6 +88,97 @@ angular.module('acServerManager') }); } }) + .controller('TemplatesCtrl', function ($scope, $filter, $timeout, TemplateService) { + $scope.templateList = []; + + TemplateService.GetTemplates(function (data) { + $scope.templateList = data; + }); + + $scope.loadTemplate = function(index) { + if (!confirm('Are you sure?')) { + return; + } + try { + var loaded = true; + var template = $scope.templateList[index]; + TemplateService.LoadTemplate(template, function(result) { + if (!(result[0] === 'O' && result[1] === 'K')) { + loaded = false; + } + }); + if (loaded) { + createAlert('success', 'Loaded succesfully, remember to restart server!', 'pe-7s-star'); + } else { + createAlert('warning', 'Load failed!', 'pe-7s-close-circle'); + } + } catch (e) { + console.log('Error - ' + e); + } + } + + $scope.removeTemplate = function(index) { + if (!confirm('Are you sure?')) { + return; + } + try { + var removed = true; + var template = $scope.templateList[index]; + TemplateService.RemoveTemplate(template, function(result) { + if (!(result[0] === 'O' && result[1] === 'K')) { + removed = false; + } else { + $scope.templateList.splice(index, 1) + } + }); + if (removed) { + createAlert('success', 'Removed succesfully', 'pe-7s-star'); + } else { + createAlert('warning', 'Remove failed', 'pe-7s-close-circle'); + } + } catch (e) { + console.log('Error - ' + e); + } + } + + $scope.saveCurrent = function() { + $scope.$broadcast('show-errors-check-validity'); + + if ($scope.form.$invalid) { + createAlert('warning', 'There are errors on the form', 'pe-7s-note'); + return; + } + + try { + var saved = true; + var template = angular.copy($scope.newTemplate); + TemplateService.SaveCurrent(template, function(result) { + if (!(result[0] === 'O' && result[1] === 'K')) { + saved = false; + } else { + $scope.templateList.push(template); + } + }); + if (saved) { + createAlert('success', 'Saved succesfully', 'pe-7s-star'); + } else { + createAlert('warning', 'Save failed', 'pe-7s-close-circle'); + } + } catch (e) { + console.log('Error - ' + e); + } + } + + function createAlert(type, msg, icon) { + $.notify({ + icon: icon, + message: msg + },{ + type: type, + timer: 3000 + }); + } + }) .controller('ServerCtrl', function ($scope, $filter, $timeout, CarService, TrackService, ServerService, BookService, PracticeService, QualifyService, RaceService, TyreService, WeatherService) { $scope.sessions = []; $scope.alerts = []; @@ -763,4 +854,4 @@ angular.module('acServerManager') } }) .controller('HelpCtrl', function($scope) { - }); \ No newline at end of file + }); diff --git a/frontend/app/services.js b/frontend/app/services.js index 1360576..60c54e3 100644 --- a/frontend/app/services.js +++ b/frontend/app/services.js @@ -275,4 +275,32 @@ angular.module('acServerManager.services', ['ngResource']). }); } }; - }); \ No newline at end of file + }). + factory('TemplateService', function($resource) { + return { + GetTemplates: function(callback) { + var resource = $resource('/api/templates'); + var result = resource.query(function() { + callback(result); + }); + }, + LoadTemplate: function(template, callback) { + var resource = $resource('/api/templates/:uuid'); + var result = resource.save({uuid: template.uuid}, template, function() { + callback(result); + }); + }, + RemoveTemplate: function(template, callback) { + var resource = $resource('/api/templates/:uuid'); + var result = resource.delete({uuid: template.uuid}, function() { + callback(result); + }); + }, + SaveCurrent: function(template, callback) { + var resource = $resource('/api/templates'); + var result = resource.save(template, function() { + callback(result); + }); + } + }; + }); diff --git a/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/config.json b/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/config.json new file mode 100644 index 0000000..4c63cc4 --- /dev/null +++ b/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/config.json @@ -0,0 +1 @@ +{"name":"Default configuration","description":"Assetto Corsa Dedicated Server default configuration"} diff --git a/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/entry_list.ini b/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/entry_list.ini new file mode 100755 index 0000000..a977568 --- /dev/null +++ b/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/entry_list.ini @@ -0,0 +1,197 @@ +[CAR_0] +MODEL=bmw_m3_e30 +SKIN=alpine_white_I +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST= + +[CAR_1] +MODEL=bmw_m3_e30 +SKIN=Brilliant_red +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_2] +MODEL=bmw_m3_e30 +SKIN=Cinnabar_red +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_3] +MODEL=bmw_m3_e30 +SKIN=0_Diamond_Black_metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_4] +MODEL=bmw_m3_e30 +SKIN=Henna_Red +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_5] +MODEL=bmw_m3_e30 +SKIN=Jet_Black +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_6] +MODEL=bmw_m3_e30 +SKIN=Macau_Blue_Metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_7] +MODEL=bmw_m3_e30 +SKIN=Misano_red +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_8] +MODEL=bmw_m3_e30 +SKIN=Nogaro_Silver_metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_9] +MODEL=bmw_m3_e30 +SKIN=Salmon_Silver_metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_10] +MODEL=bmw_m3_e30 +SKIN=Sterling_Silver_metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_11] +MODEL=bmw_m3_e30 +SKIN=alpine_white_I +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_12] +MODEL=bmw_m3_e30 +SKIN=Brilliant_red +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_13] +MODEL=bmw_m3_e30 +SKIN=Cinnabar_red +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_14] +MODEL=bmw_m3_e30 +SKIN=0_Diamond_Black_metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_15] +MODEL=bmw_m3_e30 +SKIN=Henna_Red +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_16] +MODEL=bmw_m3_e30 +SKIN=Jet_Black +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_17] +MODEL=bmw_m3_e30 +SKIN=Macau_Blue_Metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_18] +MODEL=bmw_m3_e30 +SKIN=Misano_red +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_19] +MODEL=bmw_m3_e30 +SKIN=Nogaro_Silver_metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_20] +MODEL=bmw_m3_e30 +SKIN=Salmon_Silver_metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 + +[CAR_21] +MODEL=bmw_m3_e30 +SKIN=Sterling_Silver_metallic +SPECTATOR_MODE=0 +DRIVERNAME= +TEAM= +GUID= +BALLAST=0 diff --git a/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/server_cfg.ini b/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/server_cfg.ini new file mode 100755 index 0000000..7df00fc --- /dev/null +++ b/frontend/content/templates/c7dcd596-2c3c-4598-b20d-97f7a34dae84/server_cfg.ini @@ -0,0 +1,74 @@ +[SERVER] +NAME=AC_Server +CARS=bmw_m3_e30 +CONFIG_TRACK= +TRACK=magione +SUN_ANGLE=48 +PASSWORD= +ADMIN_PASSWORD=mypassword +UDP_PORT=9600 +TCP_PORT=9600 +HTTP_PORT=8081 +PICKUP_MODE_ENABLED=1 +LOOP_MODE=1 +SLEEP_TIME=1 +CLIENT_SEND_INTERVAL_HZ=18 +SEND_BUFFER_SIZE=0 +RECV_BUFFER_SIZE=0 +RACE_OVER_TIME=180 +KICK_QUORUM=85 +VOTING_QUORUM=80 +VOTE_DURATION=20 +BLACKLIST_MODE=1 +FUEL_RATE=100 +DAMAGE_MULTIPLIER=100 +TYRE_WEAR_RATE=100 +ALLOWED_TYRES_OUT=2 +ABS_ALLOWED=1 +TC_ALLOWED=1 +STABILITY_ALLOWED=0 +AUTOCLUTCH_ALLOWED=0 +TYRE_BLANKETS_ALLOWED=0 +FORCE_VIRTUAL_MIRROR=1 +REGISTER_TO_LOBBY=1 +MAX_CLIENTS=18 +UDP_PLUGIN_LOCAL_PORT=0 +UDP_PLUGIN_ADDRESS= +AUTH_PLUGIN_ADDRESS= +LEGAL_TYRES=SV + +[PRACTICE] +NAME=Practice +TIME=10 +IS_OPEN=1 + +[QUALIFY] +NAME=Qualify +TIME=10 +IS_OPEN=1 + +[RACE] +NAME=Race +LAPS=5 +WAIT_TIME=60 +IS_OPEN=1 + +[DYNAMIC_TRACK] +SESSION_START=89 +RANDOMNESS=3 +SESSION_TRANSFER=80 +LAP_GAIN=50 + +[WEATHER_0] +GRAPHICS=3_clear +BASE_TEMPERATURE_AMBIENT=18 +BASE_TEMPERATURE_ROAD=6 +VARIATION_AMBIENT=1 +VARIATION_ROAD=1 + +[WEATHER_1] +GRAPHICS=7_heavy_clouds +BASE_TEMPERATURE_AMBIENT=15 +BASE_TEMPERATURE_ROAD=-1 +VARIATION_AMBIENT=1 +VARIATION_ROAD=1 diff --git a/frontend/entrylist.html b/frontend/entrylist.html index 4741110..31b5c4a 100644 --- a/frontend/entrylist.html +++ b/frontend/entrylist.html @@ -67,6 +67,12 @@

    Advanced

  • +
  • + + +

    Templates

    +
    +
  • diff --git a/frontend/help.html b/frontend/help.html index 0f21576..e2bf409 100755 --- a/frontend/help.html +++ b/frontend/help.html @@ -66,6 +66,12 @@

    Advanced

  • +
  • + + +

    Templates

    +
    +
  • diff --git a/frontend/index.html b/frontend/index.html index 49ed806..601f320 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -66,6 +66,12 @@

    Advanced

  • +
  • + + +

    Templates

    +
    +
  • diff --git a/frontend/rules.html b/frontend/rules.html index b8dfd7f..464982f 100644 --- a/frontend/rules.html +++ b/frontend/rules.html @@ -67,6 +67,12 @@

    Advanced

  • +
  • + + +

    Templates

    +
    +
  • diff --git a/frontend/server.html b/frontend/server.html index 6b8bf15..4fd52fe 100644 --- a/frontend/server.html +++ b/frontend/server.html @@ -67,6 +67,12 @@

    Advanced

  • +
  • + + +

    Templates

    +
    +
  • diff --git a/frontend/templates.html b/frontend/templates.html new file mode 100644 index 0000000..8401761 --- /dev/null +++ b/frontend/templates.html @@ -0,0 +1,219 @@ + + + + + + + + Assetto Corsa Server Manager + + + + + + + + + + + + + + + + + + + +
    + + +
    + + +
    +
    +
    + {{alert.msg}} +
    +
    +
    + Save current configuration +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Load saved template +
    +
    +
    + + + + + + + + + + + + + + + + + + + +
    #NameDescription
    {{$index + 1}}{{template.name}}{{template.description}} + + + + +
    +
    +
    +
    +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + diff --git a/package.json b/package.json index 77f0fcc..6227b30 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "basic-auth-connect": "^1.0.0", "jsonfile": "^2.4.0", "util": "^0.10.3", + "uuid": "^3.1.0", "node.extend": "^1.1.6" } -} \ No newline at end of file +} diff --git a/server.js b/server.js index f2c5945..4e4177c 100644 --- a/server.js +++ b/server.js @@ -7,6 +7,7 @@ var childProcess = require('child_process'); var basicAuth = require('basic-auth-connect'); var jsonfile = require('jsonfile'); var util = require('util'); +var uuidv4 = require('uuid/v4'); var extend = require('node.extend'); var settings = require('./settings'); @@ -1020,6 +1021,107 @@ app.post('/api/strackerserver/stop', function (req, res) { } }); +// list templates +app.get('/api/templates', function (req, res) { + try { + contentPath = checkLocalContentPath(contentPath); + var templateUuids = fs.readdirSync(contentPath + '/templates'); + var templates = []; + + for (var idx in templateUuids) { + var uuid = templateUuids[idx]; + var templateFile = contentPath + '/templates/' + uuid + '/config.json'; + var template = jsonfile.readFileSync(templateFile) + template.uuid = uuid; + templates.push(template); + } + + res.status(200); + res.send(templates); + } catch (e) { + console.log('Error: GET/api/templates - ' + e); + res.status(500); + res.send('Application error'); + } +}); + +// store current configuration to a new template +app.post('/api/templates', function (req, res) { + try { + contentPath = checkLocalContentPath(contentPath); + var uuid = uuidv4(); + var template = { + name: req.body.name, + description: req.body.description + } + if (template.name === '') { + throw 'Template must have a name!'; + } + + var templateDir = contentPath + '/templates/' + uuid; + // TODO: assert existence and generate new uuidv4? + fs.mkdirSync(templateDir); + fs.copyFileSync(serverPath + 'cfg/server_cfg.ini', templateDir + '/server_cfg.ini'); + fs.copyFileSync(serverPath + 'cfg/entry_list.ini', templateDir + '/entry_list.ini'); + jsonfile.writeFileSync(templateDir + '/config.json', template); + + res.status(200); + res.send('OK'); + } catch (e) { + console.log('Error: POST/api/templates - ' + e); + res.status(500); + res.send('Application error'); + } +}); + +// apply templated configuration +app.post('/api/templates/:uuid', function (req, res) { + try { + var uuid = req.params.uuid; + if (!uuid) { + throw 'UUID not provided'; + } + + contentPath = checkLocalContentPath(contentPath); + var templateDir = contentPath + '/templates/' + uuid; + config = multiLine.read(templateDir + '/server_cfg.ini', {encoding: 'utf8'}); + entryList = multiLine.read(templateDir + '/entry_list.ini', {encoding: 'utf8'}); + saveConfig(); + saveEntryList(); + + res.status(200); + res.send('OK'); + } catch (e) { + console.log('Error: PUT/api/templates - ' + e); + res.status(500); + res.send('Application error'); + } +}); + +// delete template based on uuid +app.delete('/api/templates/:uuid', function (req, res) { + try { + var uuid = req.params.uuid; + if (!uuid) { + throw 'UUID not provided'; + } + + contentPath = checkLocalContentPath(contentPath); + var templateDir = contentPath + '/templates/' + uuid; + fs.unlinkSync(templateDir + '/server_cfg.ini'); + fs.unlinkSync(templateDir + '/entry_list.ini'); + fs.unlinkSync(templateDir + '/config.json'); + fs.rmdirSync(templateDir); + + res.status(200); + res.send('OK'); + } catch (e) { + console.log('Error: DELETE/api/templates - ' + e); + res.status(500); + res.send('Application error'); + } +}); + // get fronend index page app.get('*', function (req, res) { @@ -1027,4 +1129,4 @@ app.get('*', function (req, res) { }); // server port node.js -app.listen(settings.port); \ No newline at end of file +app.listen(settings.port);