├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml └── riffapi-local.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11.3-slim 2 | RUN mkdir /app 3 | ADD riffapi-local.py /app 4 | WORKDIR /app 5 | RUN pip install Flask 6 | EXPOSE 5144/tcp 7 | CMD ["python3", "riffapi-local.py"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RiffApi Local 2 | 3 | This project is a basic local server for the [Riff Racer](https://store.steampowered.com/app/351990) game. Since the servers of the game have been shut down, it is now impossible to create and play new tracks even if you wish to play in single player - thus a server implementation is necessary. 4 | 5 | ## Requirements 6 | 7 | - Python 3.6+ 8 | - Flask 9 | 10 | (Or Docker) 11 | 12 | ## Usage 13 | 14 | ### Patching ParseOctane.dll 15 | 16 | Patch `ParseOctane.dll` to use RiffApi Local changing "https://parseapi.back4app.com" with "http://127.000000000.0.1:5144". 17 | 18 | You can do so with sed (`sed -i "s/https:\/\/parseapi.back4app.com/http:\/\/127.000000000.0.1:5144/g" ParseOctane.dll`), a hex editor, or by simply using the [website](https://riffapi.hobune.stream/). 19 | 20 | ### Running the API 21 | 22 | There are multiple ways to run the server: 23 | 24 | - Run with Python (recommended) 25 | 1. Install [Python 3.6+](https://www.python.org/downloads/). 26 | 2. Install [Flask](https://pypi.org/project/Flask/) with `pip install Flask`. 27 | 3. Run `riffapi-local.py`. 28 | 29 | - Run with Docker. 30 | 1. Install Docker. 31 | 2. Run with `docker compose up --build`. 32 | 33 | **Important**: keep the command prompt open until you exit the game. 34 | 35 | ## riffapi.hobune.stream 36 | 37 | A more advanced online server with historic data is available at [riffapi.hobune.stream](https://riffapi.hobune.stream/). This server implementation here is for those wishing to run their own local server or in case the hobune server ever goes down. 38 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | app: 5 | build: . 6 | ports: 7 | - "5144:5144" 8 | -------------------------------------------------------------------------------- /riffapi-local.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | import string 3 | import random 4 | app = Flask(__name__) 5 | 6 | news = { 7 | "title": "RiffApiLocal", 8 | "msg": "You're connected to RiffApiLocal.\nOnline features are unavailable.\nNo data will be saved.", 9 | } 10 | username = "RiffApiLocal" 11 | 12 | responses = { 13 | "Configuration": '{"results":[{"objectId":"ITz5K79wcQ","updatedAt":"2015-10-06T15:37:10.761Z","RegionGroup":"Default","createdAt":"2015-08-27T16:48:23.993Z","OnTrackCoinValue":10,"TrackConversionCost":300,"ShareScoreCoinReward":200,"WatchAdCoinReward":200,"TrackCreatorCoinReward":100}]}', 14 | "News": f'{{"results":[{{"objectId":"RiffApiLocalNews","updatedAt":"2000-01-01T00:00:00.000Z","de":"{news["msg"]}","createdAt":"2000-01-01T00:00:00.000Z","ru":"{news["msg"]}","en":"{news["msg"]}","esTitle":"{news["title"]}","it":"{news["msg"]}","fr":"{news["msg"]}","frTitle":"{news["title"]}","itTitle":"{news["title"]}","es":"{news["msg"]}","enTitle":"{news["title"]}","Status":"Active","ruTitle":"{news["title"]}","deTitle":"{news["title"]}","Pinned":true,"ID":16}}]}}', 15 | "Profile_Steam": f'{{"results":[{{"objectId":"{username}Profile","SystemID":"1","FavouriteCar":1,"FavouriteSong":"null","FavouriteGenre":"pop","DriverLevel":0,"Platform":"PC","Username":"{username}","createdAt":"2000-01-01T00:00:00.000Z","updatedAt":"2000-01-01T00:00:00.000Z"}}]}}', 16 | "Version": '{"results":[{"objectId":"BoJMZ1TQJZ","updatedAt":"2015-02-10T21:05:53.214Z","Ver":100,"createdAt":"2015-02-10T20:50:24.164Z"}]}', 17 | "_Installation": '{"opens":1,"updatedAt":"2000-01-01T00:00:00.000Z"}' 18 | } 19 | 20 | @app.route('/events/AppOpened', methods=['GET', 'POST']) 21 | def AppOpened(): 22 | return '{}' 23 | 24 | @app.route('/files/', methods=['GET', 'POST']) 25 | def Files(file): 26 | return '{"url":"http://127.0.0.1/file.bin","name":"file.bin"}' 27 | 28 | @app.route('/classes/', methods=['GET']) 29 | def ClassEventGET(event): 30 | return responses.get(event, '{"results":[]}') 31 | 32 | @app.route('/classes/', methods=['POST', 'PUT']) 33 | def ClassEventPOST(event): 34 | return f'{{"objectId":"{"".join(random.choice(string.ascii_lowercase) for _ in range(10))}","createdAt":"2000-01-01T00:00:00.000Z"}}' 35 | 36 | if __name__ == '__main__': 37 | app.run(host="127.0.0.1", port=5144, debug=False) 38 | 39 | # sed -i "s/https:\/\/parseapi.back4app.com/http:\/\/127.000000000.0.1:5144/g" ParseOctane.dll 40 | --------------------------------------------------------------------------------