diff --git a/.gradle/7.5.1/checksums/checksums.lock b/.gradle/7.5.1/checksums/checksums.lock index f787054..77ab266 100644 Binary files a/.gradle/7.5.1/checksums/checksums.lock and b/.gradle/7.5.1/checksums/checksums.lock differ diff --git a/.gradle/7.5.1/checksums/md5-checksums.bin b/.gradle/7.5.1/checksums/md5-checksums.bin index c750b26..bb72729 100644 Binary files a/.gradle/7.5.1/checksums/md5-checksums.bin and b/.gradle/7.5.1/checksums/md5-checksums.bin differ diff --git a/.gradle/7.5.1/checksums/sha1-checksums.bin b/.gradle/7.5.1/checksums/sha1-checksums.bin index cfc5d38..22476d6 100644 Binary files a/.gradle/7.5.1/checksums/sha1-checksums.bin and b/.gradle/7.5.1/checksums/sha1-checksums.bin differ diff --git a/.gradle/7.5.1/executionHistory/executionHistory.bin b/.gradle/7.5.1/executionHistory/executionHistory.bin index 1f2fe53..8f4a234 100644 Binary files a/.gradle/7.5.1/executionHistory/executionHistory.bin and b/.gradle/7.5.1/executionHistory/executionHistory.bin differ diff --git a/.gradle/7.5.1/executionHistory/executionHistory.lock b/.gradle/7.5.1/executionHistory/executionHistory.lock index 31b3a22..be5d250 100644 Binary files a/.gradle/7.5.1/executionHistory/executionHistory.lock and b/.gradle/7.5.1/executionHistory/executionHistory.lock differ diff --git a/.gradle/7.5.1/fileHashes/fileHashes.bin b/.gradle/7.5.1/fileHashes/fileHashes.bin index 4779624..007d47b 100644 Binary files a/.gradle/7.5.1/fileHashes/fileHashes.bin and b/.gradle/7.5.1/fileHashes/fileHashes.bin differ diff --git a/.gradle/7.5.1/fileHashes/fileHashes.lock b/.gradle/7.5.1/fileHashes/fileHashes.lock index e006ae3..2c1fe84 100644 Binary files a/.gradle/7.5.1/fileHashes/fileHashes.lock and b/.gradle/7.5.1/fileHashes/fileHashes.lock differ diff --git a/.gradle/7.5.1/fileHashes/resourceHashesCache.bin b/.gradle/7.5.1/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000..ac8968c Binary files /dev/null and b/.gradle/7.5.1/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 0f84633..0747b15 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin index 81a9657..60dc4e6 100644 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index 934731b..973ea0f 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/Database Backup/steam_backUp b/Database Backup/steam_backUp new file mode 100644 index 0000000..4ff418f Binary files /dev/null and b/Database Backup/steam_backUp differ diff --git a/README.md b/README.md index 78c6dff..97df100 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,12 @@ - -

- - - -

- +# In the name of god +# Steam simulation ## Introduction - -In this assignment you are tasked with creating a Java application designed to handle the management and download of video game data, similar to Steam. Steam is an online platform that distributes video games in a digital format. -- The application you will develop is simpler in design. It doesn't need to have many of the features available in Steam, such as the ability to purchase or trade video games. - -The app consists of separate Server and Client components, connected to each other using socket programming. The Server component maintains a database to store information, and the Client side of the app allows clients to request information and files from the server. - - -## Objectives -- Review the concepts of socket programming and database management. -- Create a local Server to store information about video games and user accounts. -- Allow clients to create accounts, log in, and log out. -- Use a hashing algorithm to implement secure password storage. -- Enable clients to download video game files (represented by PNG files for simplicity) from the Server. -- Write a detailed report on the assignment. - -## Tasks -Ensure to Fork this repository and clone the fork to your local machine. Create a new Git branch before starting your work. - -### 1. Design the application's Client-Server API -The Client and Server sides of the app should be able to communicate with each other in a predetermined manner. In order to achieve this, a series of Request-Response interactions between the Client and the Server can be implemented. - -#### 1.1. Request -A Request must have these features: -- A Request is sent from the Client to the Server over the network. -- You should create different types of Requests for different actions. -- A Client can Request a list of available games, info about a specific game, or to download a game. Creating an account, logging in, and logging out can also be considered Requests. -- It is up to you to design the Request's format. For example, A Request can be a JSON string which is easy to send on a socket. - -#### 1.2. Response -A Response must have these features: -- A Response is sent from the Server to the Client over the network. -- You should create different Response types corresponding to the received Request. -- Attach appropriate data to the Response based on the Request. A Response might contain the data a user has requested, or it might just be a boolean indicating the result of a previously sent Request (such as confirming a user's login attempt by returning a True boolean). -- It is up to you to design the Response's format. Similar to a Request, a Response can also be a JSON string. - -Note that each Request received from the Client must be answered with a Response from the Server. Include details about how you designed the Request-Response interactions in your report. - -### 2. **Design the application's architecture** -The Client and Server are the two main components of your app. They must be connected through the use of a socket connection. -
Plan out your project's files accordingly. Any file designed for the Client side must be stored in the `Client` package and any file designed for the Server side must be stored in the `Server` package. Classes that can be used by both sides should be stored in the `Shared` package. - -#### 2.1. Client -The Client component must provide a graphical or command-line interface for users to interact with the app. It should allow users to: -- Create accounts by providing a username, a password, and a date of birth (DOB) -- Log in and log out securely (more info in [section 5](#5-use-a-hashing-algorithm-to-provide-security-for-accounts)) -- Browse the available video game catalog -- View each individual game's details -- Download video game files and manage these downloads - -
The Client must provide the following functionalities: - -- Socket Connection: Establish a connection with the Server over the local network using socket programming. -- Request Generator: Generate Requests based on the user's input. -- Response Handler: After sending a Request, wait to receive a Response from the Server. Then provide the user with appropriate info based on the response type. -- Download Manager: If a user requests to download a game, the Download Manager ensures the file is downloaded successfully and stores it in the correct format in a specified directory. - - -#### 2.2. Server -The Server component is responsible for handling Client requests, managing the database, and sending video game files to the Client. Before a Server is ready to accept clients, it must connect to the database to access the stored information: -- At the start of the first run of your Server, it must read data from the files located in the `Resources` folder and import it to the database. This process is explained in more detail in [section 4](#4-import-the-necessary-data-from-the-resources-folder). -- Your program must run a query on the database according to the received Request. -- Every time a new account is created, the account credentials must be added to the database. -- If a user requests to download a video game, you must update the number of times that the game has been downloaded by that user. - -
The Server must provide the following functionalities: - -- Socket Listener: Listen for incoming Client connections and redirect requests to the appropriate handlers. Once a request has been fully handled, enter listening mode again. -- Request Handlers: Process Client Requests and interact with the database to fetch the requested data. You may need to create multiple handlers for various Requests. -- Database Manager: Interact with the database system to perform CRUD (Create, Read, Update, Delete) operations. (more info in [section 3](#3-create-a-database-to-store-the-apps-data-persistently)) -- Response Generator: The final step in handling a Request is to send an appropriate Response to the Client. Attach the needed data to the Response based on the Request. -- Logging: Try to log every major action the Server performs (e.g. accepting a Client, sending a file, etc.) to simplify the debugging process. - - -### 3. **Create a Database to store the app's data persistently** -The Server's database plays a central role in storing essential data. You are allowed to use a SQL-based database or a NoSQL database (such as MongoDB). Remember to add the necessary `JDBC` (Java Database Connectivity) dependency to your project. -
The database must contain the following data: - - -#### 3.1. Games +Steam is a digital distribution platform developed by Valve Corporation that allows users to purchase, download, and play video games on their computers. +In this code we have simulation of steam. We have database, server and client and request, response between them. Instead playing game, users can download pictures of game +## Desgin and implementation +- Code has 4 classes (database, serverMain, client handler and clientMain) and a folder with information and pictures of games. + `Database` is postgresql and makes tables are made in database class. Database contains the following data: +### Games This table stores information about video games, including their attributes. | Column name | Data type | Description | @@ -101,18 +23,18 @@ This table stores information about video games, including their attributes. | file_path | text | Path of the game file stored in the `Resources` folder | -#### 3.2. Accounts +### Accounts This table stores information about user accounts. | Column name | Data type | Description | |---------------|-----------|-------------------------------------| | id | text | A unique identifier for the account | | username | text | Username of the account | -| password | text | Hashed password of the account | +| hash of password | text | Hashed password of the account | | date_of_birth | date | The user's date of birth | -#### 3.3. Downloads +### Downloads This table stores information about user downloads. | Column name | Data type | Description | @@ -120,84 +42,17 @@ This table stores information about user downloads. | account_id | text | A unique identifier for an existing account | | game_id | text | A unique identifier for an existing game | | download_count | Integer | The number of times a user has downloaded a specific game | +- In code, I get connection of database and used it. -
**- Note that you are allowed to change the structure and number of tables, but you are required to store all the mentioned attributes.** - -Regardless of how you implement the database, it must be able to answer questions such as: -- How many accounts have been created? -- What is the average price of the available video games? -- How many times in total has a game been downloaded? -- How many DISTINCT users have downloaded a game? - - -### 4. **Import the necessary data from the Resources folder** -- Alongside the Server component, a `Resources` folder is provided that stores 10 TXT files (each storing a game's attributes) and 10 PNG files (each representing a game's data). The TXT files and the PNG files are paired together based on their names (which is always the game id). -- Before the Server starts accepting requests from clients, it must first import all the game data from TXT files and store them in the database. This is a one-time-only process, once the data has been successfully imported to the database, the Server doesn't need to import the data on subsequent runs. -- Ensure to store each PNG file's path in the `file_path` column of the `Games` table. Whenever a user requests to download a specific video game, the Server must send the respective PNG file to the Client. The Client then stores that file in the `Downloads` folder -- Each TXT file is structured like this: - - gameid - - title - - developer - - genre - - price - - release_year - - controller_support - - reviews - - size - -
Here's an example of a TXT file: - > 2050650 -
Resident Evil 4 -
Capcom -
Survival Horror -
59.99 -
2023 -
True -
97 -
618 - - -### 5. **Use a hashing algorithm to provide security for accounts** -- To ensure the security of user accounts, the app should implement password hashing. When a user creates an account or changes their password, apply a hashing algorithm (e.g. bcrypt) to transform the password into a secure, irreversible form. The hashed passwords must then be stored in the database. DO NOT store the plain password in the database. -- Note that when an existing user tries to log in, the password they provide must be hashed again and compared to the hashed password that is stored in the database. Login is successful only if the two hashed values are equal. -- You can add the `JBCrypt` package to your dependencies and use the hash functions provided in the package. - - -### 6. **Provide a backup of the database you created** -- There are multiple ways to back up your database based on the DBMS you're using. -- If you're using PostgreSQL for example, you can open pgAdmin, right-click on your database, and choose the Backup option to save a copy of your database. -- Place the backup file in the project's `Database Backup` folder. - - -#### 7. Commit your changes and push your commits to your fork on GitHub. Create a pull request (assigned to your mentor) to merge your changes to the main branch of your fork on GitHub. - - -## Notes -- You are allowed to modify or delete all the provided code. Add as many classes and tables as you need. Do not limit yourself to the existing classes and methods. -- You are NOT allowed to change the contents of the `Resources` folder located on the Server side. -- Note that the Client side of the program cannot access the database directly and must connect to the Server to ask for data. -- If you decide to use JSON strings in your program, you may find the `gson` package to be useful for serializing a Java object to JSON. -- Your report should include details on the architecture you used for the app, as well as the design process of your database. Provide adequate explanation regarding each component of the app. Specify the primary key chosen for each table and how you ensured it would be unique. - - -## Bonus Objectives -1. Add a GUI (Graphical User Interface) to your project. It's recommended to use JavaFX. This GUI should include all the options offered by the command line menu you implemented earlier. Once a download is complete, display the PNG picture received from the database in the GUI. -2. Use multithreading in the Server side code to allow multiple clients to connect concurrently. This can be done by creating a main listener thread that waits for connections from clients. Once a Client has connected, create a new thread to handle that specific Client. -3. Use multithreading to speed up the download process by creating multiple data streams that run in parallel to each other. -4. Implement an age restriction system for video games. Prevent users who are younger than a specified age to access certain games. You can add a new age restriction column to the `Games` table. - - -## Evaluation -- Your code should compile and run without any errors -- Your code should be well-organized, readable, properly commented, and should follow clean code principles -- Your Database should be well-structured with as little data redundancy as possible -- You should use Git for version control and include meaningful commit messages - - -## Submission -- Push your code to your fork on GitHub -- Upload a backup of your database to your fork on GitHub -- Upload your report to your fork on GitHub +- `serverMain` has a while(true) and it listen for client. When client accept, it makes a thread and run it to client handler. +- `clientHandler` is a thread and and has menu and push clients into that. In menu it get order's client and recognized it in switch case and send it to its function. +- `clientMain` has a login menu contains signIn,signUp and exit. When user sginIn, the menu has list of games, game information, download game and exit. +- For download, serverHandler gets file path from database and breaks file into chunks and sends it to client. Then updates download_count in downloads table. Client saves file in download folder. +## Testing and evaluation +- In order to test my program, I tried diffrent inputs and tracked the program flow. I used breakpoints and other tools to achive my goal. +## Conclusion +- In conclusion, this project was a valuable learning experience that allowed me to gain a deeper understanding of Java programming and multi threading concepts and web sockets. +- Throughout the development process, I encountered several challenges, including design considerations, testing and debugging. However, I was able to overcome these obstacles through careful planning, research, and collaboration with my peers. -The deadline for submitting your code is Wednesday, May 31 (10th of Khordad). Good luck and happy coding! +- Although this project was successful in achieving its objectives, there is still room for improvement. Future work could include expanding the functionality of the application, improving the user interface and adding GUI, and optimizing performance. \ No newline at end of file diff --git a/REPORT.md b/REPORT.md new file mode 100644 index 0000000..97df100 --- /dev/null +++ b/REPORT.md @@ -0,0 +1,58 @@ +# In the name of god +# Steam simulation +## Introduction +Steam is a digital distribution platform developed by Valve Corporation that allows users to purchase, download, and play video games on their computers. +In this code we have simulation of steam. We have database, server and client and request, response between them. Instead playing game, users can download pictures of game +## Desgin and implementation +- Code has 4 classes (database, serverMain, client handler and clientMain) and a folder with information and pictures of games. + `Database` is postgresql and makes tables are made in database class. Database contains the following data: +### Games +This table stores information about video games, including their attributes. + +| Column name | Data type | Description | +|--------------------|------------------|-------------------------------------------------------------------------------------| +| id | text | A unique identifier for the game | +| title | text | Title of the video game | +| developer | text | Name of the studio that developed the game | +| genre | text | Genre of the video game | +| price | double precision | Current price of the video game | +| release_year | integer | The game's release year | +| controller_support | boolean | This parameter is True if the game supports controllers | +| reviews | integer | A value from 0 to 100 indicating the game's average user score on Steam (higher is better) | +| size | integer | Size of the game in kilobytes | +| file_path | text | Path of the game file stored in the `Resources` folder | + + +### Accounts +This table stores information about user accounts. + +| Column name | Data type | Description | +|---------------|-----------|-------------------------------------| +| id | text | A unique identifier for the account | +| username | text | Username of the account | +| hash of password | text | Hashed password of the account | +| date_of_birth | date | The user's date of birth | + + +### Downloads +This table stores information about user downloads. + +| Column name | Data type | Description | +|----------------|-----------|--------------------------------------------------| +| account_id | text | A unique identifier for an existing account | +| game_id | text | A unique identifier for an existing game | +| download_count | Integer | The number of times a user has downloaded a specific game | +- In code, I get connection of database and used it. + +- `serverMain` has a while(true) and it listen for client. When client accept, it makes a thread and run it to client handler. +- `clientHandler` is a thread and and has menu and push clients into that. In menu it get order's client and recognized it in switch case and send it to its function. +- `clientMain` has a login menu contains signIn,signUp and exit. When user sginIn, the menu has list of games, game information, download game and exit. +- For download, serverHandler gets file path from database and breaks file into chunks and sends it to client. Then updates download_count in downloads table. Client saves file in download folder. +## Testing and evaluation +- In order to test my program, I tried diffrent inputs and tracked the program flow. I used breakpoints and other tools to achive my goal. +## Conclusion +- In conclusion, this project was a valuable learning experience that allowed me to gain a deeper understanding of Java programming and multi threading concepts and web sockets. + +- Throughout the development process, I encountered several challenges, including design considerations, testing and debugging. However, I was able to overcome these obstacles through careful planning, research, and collaboration with my peers. + +- Although this project was successful in achieving its objectives, there is still room for improvement. Future work could include expanding the functionality of the application, improving the user interface and adding GUI, and optimizing performance. \ No newline at end of file diff --git a/build.gradle b/build.gradle index 016cbd3..01117ec 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,9 @@ dependencies { // https://mvnrepository.com/artifact/org.postgresql/postgresql implementation group: 'org.postgresql', name: 'postgresql', version: '42.6.0' + // https://mvnrepository.com/artifact/org.json/json + implementation group: 'org.json', name: 'json', version: '20230227' + } test { diff --git a/src/main/java/Client/ClientMain.java b/src/main/java/Client/ClientMain.java index ef21d55..b0223b4 100644 --- a/src/main/java/Client/ClientMain.java +++ b/src/main/java/Client/ClientMain.java @@ -1,7 +1,204 @@ package Client; +import java.net.*; +import java.io.*; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Scanner; +import org.json.JSONObject; -public class ClientMain { - public static void main(String[] args) { +public class ClientMain +{ + private static Socket socket = null; + private static BufferedReader input = null; + private static PrintWriter out = null; + public ClientMain(String address, int port) + { + try + { + socket = new Socket(address, port); + System.out.println("Connected to server"); + + input = new BufferedReader(new InputStreamReader(socket.getInputStream())); + out = new PrintWriter(socket.getOutputStream(),true); + } + catch(UnknownHostException u) + { + System.out.println(u); + } + catch(IOException i) + { + System.out.println(i); + } + } + public static void ClientClose(){ + try + { + input.close(); + out.close(); + socket.close(); + } + catch(IOException i) + { + System.out.println(i); + } + } + + public static void RunMenu(){ + Scanner in = new Scanner(System.in); + while (true) { + System.out.print("1.Sign in\n2.Sign up\n3.Exit\n->"); + String order = in.nextLine(); + + //sign in + if (order.equals("1")) { + System.out.print("Enter username\n->"); + String username = in.nextLine(); + + try { + out.println("usernameExist"); + out.println(username); + if (input.readLine().equals("false")) System.out.println("Username doesn't exist"); + else{ + System.out.print("Enter password\n->"); + String password = in.nextLine(); + out.println("checkPass"); + out.println(username); + out.println(password); + if (input.readLine().equals("false")) System.out.println("password is not correct"); + else signin(username); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + //sign up + else if (order.equals("2")) { + System.out.print("Enter username\n->"); + String username= in.nextLine(); + System.out.println(username); + try { + out.println("usernameExist"); + out.println(username); + String foo = input.readLine(); + if (foo.equals("true")) System.out.println("Username existed"); + else{ + System.out.print("Enter password\n->"); + String password= in.nextLine(); + String date; + while (true){ + System.out.print("Enter date of birthday(format : MM-dd-yyyy)\n->"); + date= in.nextLine(); + DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy"); + dateFormat.setLenient(false); + try { + dateFormat.parse(date); + break; + } catch (ParseException e){ + System.out.println("Format is wrong!"); + } + } + + JSONObject jsonObj = new JSONObject(); + out.println("signUp"); + jsonObj.put("username", username); + jsonObj.put("password", password); + jsonObj.put("date",date); + + out.println(jsonObj); + System.out.println("Account create"); + + } + } catch (IOException e) { + e.printStackTrace(); + } + } + //exit + else if (order.equals("3")) { + out.println("exit"); + break; + }else System.out.println("Wrong order!"); + } + + } + public static void signin(String username){ + Scanner in = new Scanner(System.in); + while (true) { + System.out.print("1.List of games\n2.Game information\n3.Download game\n4.Exit\n->"); + String order = in.nextLine(); + + //list of games + if (order.equals("1")) { + try { + out.println("listOfGames"); + System.out.println(input.readLine()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + //game information + else if (order.equals("2")) { + try { + System.out.print("Enter name of game\n->"); + String gameName = in.nextLine(); + out.println("checkGameName"); + out.println(gameName); + + if (input.readLine().equals("false")) System.out.println("This game does not exist"); + else { + out.println("gameInfo"); + out.println(gameName); + String jsonString = input.readLine(); + System.out.println(jsonString); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + //download game + else if (order.equals("3")) { + try { + System.out.println("Enter name of game"); + String gameName = in.nextLine(); + out.println("checkGameName"); + out.println(gameName); + if (input.readLine().equals("false")) System.out.println("this game doesn't exist"); + else { + out.println("Download"); + out.println(gameName); + out.println(username); + + DataInputStream dataInputStream = new DataInputStream(socket.getInputStream()); + int bytes = 0; + FileOutputStream fileOutputStream = new FileOutputStream("/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Client/Downloads/"+gameName+".png"); + long size = dataInputStream.readLong(); // read file size + byte[] buffer = new byte[4 * 1024]; + while (size > 0 && (bytes = dataInputStream.read(buffer, 0, (int)Math.min(buffer.length, size))) != -1) { + // Here we write the file using write method + fileOutputStream.write(buffer, 0, bytes); + size -= bytes; // read upto file size + } + System.out.println("File is Received"); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + //exit + else if (order.equals("4")) { + break; + }else System.out.println("wrong order!"); + } + } + public static void main(String args[]) + { + ClientMain client = new ClientMain("127.0.0.1", 1402); + RunMenu(); + ClientClose(); + System.out.println("Client closed"); } } diff --git a/src/main/java/Client/Downloads/Frostpunk.png b/src/main/java/Client/Downloads/Frostpunk.png new file mode 100644 index 0000000..1bcef1d Binary files /dev/null and b/src/main/java/Client/Downloads/Frostpunk.png differ diff --git a/src/main/java/Client/Downloads/The Witcher 3: Wild Hunt.png b/src/main/java/Client/Downloads/The Witcher 3: Wild Hunt.png new file mode 100644 index 0000000..f197153 Binary files /dev/null and b/src/main/java/Client/Downloads/The Witcher 3: Wild Hunt.png differ diff --git a/src/main/java/Server/DataBase.java b/src/main/java/Server/DataBase.java new file mode 100644 index 0000000..a9468fa --- /dev/null +++ b/src/main/java/Server/DataBase.java @@ -0,0 +1,101 @@ +package Server; + +import java.sql.*; + +public class DataBase { + private String url = "jdbc:postgresql://localhost:5432/postgres"; + private String user = "postgres"; + private String pass = "12345"; + private Connection connection; + private Statement statement; + public void connect() { + try { + connection = DriverManager.getConnection(url, user, pass); + statement = connection.createStatement(); + ResultSet resultSet = null; + + resultSet = statement.executeQuery("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'games')"); + resultSet.next(); + if (!resultSet.getBoolean(1)) { + statement.executeUpdate("CREATE table games(\n" + + "id text not null primary key,\n" + + "title text not null,\n" + + "developer text,\n" + + "genre text,\n" + + "price double precision," + + " release_year integer, \n" + + "controller_support boolean,\n" + + "reviews integer,\n" + + "size integer not null,\n" + + "file_patch text not null);\n"); + System.out.println("games table create"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('292030', 'The Witcher 3: Wild Hunt', 'CD Projekt Red', 'Role-playing', 29.99, 2015, True, 96, 798,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/292030.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('323190', 'Frostpunk', '11 bit studios', 'Strategy', 29.99, 2018, False, 91, 944,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/323190.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('359550', 'Tom Clancys Rainbow Six Siege', 'Ubisoft', 'First-person Shooter', 19.99, 2015, True, 82, 561,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/359550.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('489830', 'The Elder Scrolls V: Skyrim Special Edition', 'Bethesda Game Studios', 'First-person Shooter', 39.99, 2016, True, 96, 677,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/489830.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('1085660', 'Destiny 2', 'Bungie', 'First-person Shooter', 0.0, 2019, True, 74, 657,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/1085660.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('1151640', 'Horizon Zero Dawn Complete Edition', 'Guerrilla', 'Action-adventure', 49.99, 2020, True, 87, 915,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/1151640.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('1174180', 'Red Dead Redemption 2', 'Rockstar Games', 'Action-adventure', 59.99, 2018, True, 90, 771,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/1174180.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('1196590', 'Resident Evil Village', 'Capcom', 'Survival Horror', 39.99, 2021, True, 95, 811,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/1196590.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('1245620', 'Elden Ring', 'FromSoftware', 'Role-playing', 59.99, 2022, True, 94, 703,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/1245620.png' );"); + + statement.executeUpdate("INSERT INTO games (id, title, developer, genre, price, release_year, controller_support,reviews, size, file_patch)" + + " values ('2050650', 'Resident Evil 4', 'Capcom', 'Survival Horror', 59.99, 2023, True, 97, 618,'/home/abolfazl/Documents/AP/Eighth-Assignment-Steam/src/main/java/Server/Resources/2050650.png' );"); + + } + + //make accounts table + resultSet = statement.executeQuery("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'accounts')"); + resultSet.next(); + if (!resultSet.getBoolean(1)) { + statement.executeUpdate("CREATE table accounts( id bigserial not null primary key, username text not null, password text not null, date_of_birth date not null)"); + System.out.println("accounts table create"); + } + + //make downloads table + resultSet = statement.executeQuery("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'downloads')"); + resultSet.next(); + if (!resultSet.getBoolean(1)) { + statement.executeUpdate("CREATE table downloads( account_id bigint references accounts (id), game_id text references games (id), download_count integer not null); "); + System.out.println("downloads table create"); + } + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public void closeConnection(){ + try { + statement.close(); + connection.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public Connection getConnection() { + return connection; + } + + public Statement getStatement() { + return statement; + } +} + diff --git a/src/main/java/Server/Resources/1085660.txt b/src/main/java/Server/Resources/1085660.txt index 9239aec..08c6fae 100644 --- a/src/main/java/Server/Resources/1085660.txt +++ b/src/main/java/Server/Resources/1085660.txt @@ -1,4 +1,4 @@ -2050650 +1085660 Destiny 2 Bungie First-person Shooter diff --git a/src/main/java/Server/ServerMain.java b/src/main/java/Server/ServerMain.java index 7f3dd14..07d51bf 100644 --- a/src/main/java/Server/ServerMain.java +++ b/src/main/java/Server/ServerMain.java @@ -1,7 +1,30 @@ package Server; -public class ServerMain { - public static void main(String[] args) { +import java.net.*; +import java.io.*; +public class ServerMain{ + public static void main(String args[]) { + DataBase dataBase = new DataBase(); + dataBase.connect(); + try { + ServerSocket server = new ServerSocket(1402); + System.out.println("Server started"); + int i = 0; + while (true) + { + i++; + System.out.println("Waiting for a client ..."); + Socket socket = server.accept(); + System.out.println("Client accepted "+ socket.getRemoteSocketAddress()); + clientHandler clientHandler = new clientHandler(socket, dataBase, i); + clientHandler.start(); + } + } catch (IOException i) { + System.out.println(i); + } + + dataBase.closeConnection(); + System.out.println("database closed"); } -} +} \ No newline at end of file diff --git a/src/main/java/Server/clientHandler.java b/src/main/java/Server/clientHandler.java new file mode 100644 index 0000000..8374e73 --- /dev/null +++ b/src/main/java/Server/clientHandler.java @@ -0,0 +1,303 @@ +package Server; + +import org.json.JSONObject; +import java.io.*; +import java.net.Socket; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class clientHandler extends Thread { + private Socket socket = null; + private BufferedReader input = null; + private PrintWriter out = null; + private DataBase dataBase = new DataBase(); + private int i = 0; + + public clientHandler(Socket socket,DataBase dataBase,int i) { + this.i=i; + this.socket = socket; + this.dataBase = dataBase; + try { + input = new BufferedReader(new InputStreamReader(socket.getInputStream())); + out = new PrintWriter(socket.getOutputStream(),true); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public void run(){ + while (menu()){} + } + + public boolean menu() { + try { + String order = input.readLine(); + System.out.println("Server get order: "+order+" from client: "+i); + switch (order) { + + case "usernameExist": + String username = input.readLine(); + if (usernameExist(username) == true) out.println("true"); + else out.println("false"); + break; + + case "exit": + return false; + + case "signUp": + String jsonString = input.readLine(); + signUp(jsonString); + System.out.println("Create new acount"); + break; + + case "checkPass": + String usernam = input.readLine(); + String password = input.readLine(); + if (checkPass(usernam, password) == true) out.println("true"); + else out.println("false"); + break; + + case "listOfGames": + out.println(listOfGames()); + break; + + case "checkGameName": + String GameName = input.readLine(); + if (gameExist(GameName) == true) out.println("true"); + else out.println("false"); + break; + + + case "Download": + String gameName= input.readLine(); + String user = input.readLine(); + download(gameName, user); + break; + + case "gameInfo": + gameName = input.readLine(); + gameInfo(gameName); + break; + + default: + System.out.println("Command not recognized!"); + break; + } + + }catch (IOException i) { + System.out.println(i); + } + return true; + } + public boolean usernameExist(String username) { + try { + String query = "SELECT EXISTS(SELECT 1 FROM accounts WHERE username = ?)"; + PreparedStatement stmt = dataBase.getConnection().prepareStatement(query); + stmt.setString(1, username); + ResultSet rs = stmt.executeQuery(); + rs.next(); + return rs.getBoolean(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public boolean gameExist(String gameName){ + try { + String query = "SELECT EXISTS(SELECT 1 FROM games WHERE title = ?)"; + PreparedStatement stmt = dataBase.getConnection().prepareStatement(query); + stmt.setString(1, gameName); + ResultSet rs = stmt.executeQuery(); + rs.next(); + return rs.getBoolean(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public void signUp(String jsonString){ + + JSONObject jsonObj = new JSONObject(jsonString); + String query = "INSERT INTO accounts ( username, password, date_of_birth) values(?,?,?)"; + + try { + PreparedStatement stmt = dataBase.getConnection().prepareStatement(query); + stmt.setString(1, jsonObj.getString("username")); + stmt.setString(2, hashPassword(jsonObj.getString("password")) ); + + DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy"); + + Date date = dateFormat.parse(jsonObj.getString("date")); + stmt.setDate(3, new java.sql.Date(date.getTime()) ); + stmt.executeUpdate(); + + } catch (SQLException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + + + } + public boolean checkPass(String username ,String password){ + try { + String query="select password from accounts where username = ?"; + PreparedStatement stmt = dataBase.getConnection().prepareStatement(query); + stmt.setString(1, username); + ResultSet resultSet = stmt.executeQuery(); + resultSet.next(); + if (resultSet.getString(1).equals(hashPassword(password))) return true; + else return false; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public String listOfGames(){ + try { + + ResultSet resultSet = dataBase.getStatement().executeQuery("select title from games;"); + String listOfGames=""; + while (resultSet.next()){ + listOfGames += "\\ "; + listOfGames += resultSet.getString("title"); + } + return listOfGames; + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public void download(String gameName, String username){ + String filePath; + try { + ResultSet resultSet = dataBase.getStatement().executeQuery("select file_patch from games where title = '"+gameName+"';"); + resultSet.next(); + filePath = resultSet.getString(1); + } catch (SQLException e) { + throw new RuntimeException(e); + } + try { + int bytes = 0; + File file = new File(filePath); + FileInputStream fileInputStream = new FileInputStream(file); + DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream()); + // Here we send the File to Server + dataOutputStream.writeLong(file.length()); + // Here we break file into chunks + byte[] buffer = new byte[4 * 1024]; + while ((bytes = fileInputStream.read(buffer)) != -1) { + // Send the file to Server Socket + dataOutputStream.write(buffer, 0, bytes); + dataOutputStream.flush(); + } + fileInputStream.close(); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + + AddToDownloads(gameName, username); + } + public void AddToDownloads(String gameName, String userName ){ + try { + //get user_id + String query = "SELECT id from accounts where username = ?"; + PreparedStatement stmt = dataBase.getConnection().prepareStatement(query); + stmt.setString(1, userName); + ResultSet resultSet = stmt.executeQuery(); + resultSet.next(); + Long user_id = resultSet.getLong(1); + + //get game_id + query = "SELECT id from games where title = ?"; + stmt = dataBase.getConnection().prepareStatement(query); + stmt.setString(1, gameName); + resultSet = stmt.executeQuery(); + resultSet.next(); + String game_id = resultSet.getString(1); + + + query = "SELECT COUNT(*) FROM downloads WHERE account_id = ? AND game_id = ?"; + stmt = dataBase.getConnection().prepareStatement(query); + stmt.setLong(1, user_id); + stmt.setString(2, game_id); + resultSet = stmt.executeQuery(); + resultSet.next(); + int count = resultSet.getInt(1); + + if (count > 0){ + query = "SELECT download_count from downloads where account_id = ? AND game_id = ?"; + stmt = dataBase.getConnection().prepareStatement(query); + stmt.setLong(1, user_id); + stmt.setString(2, game_id); + resultSet = stmt.executeQuery(); + resultSet.next(); + int download_count = resultSet.getInt(1); + download_count++; + query = "UPDATE downloads SET download_count = ? WHERE account_id = ? AND game_id = ?;"; + stmt = dataBase.getConnection().prepareStatement(query); + stmt.setInt(1,download_count); + stmt.setLong(2, user_id); + stmt.setString(3, game_id); + stmt.executeUpdate(); + + System.out.println("download_count update to: " + download_count); + } + else { + query = "insert into downloads (account_id, game_id, download_count) values ( ? , ? , 1)"; + stmt = dataBase.getConnection().prepareStatement(query); + stmt.setLong(1, user_id); + stmt.setString(2, game_id); + stmt.executeUpdate(); + + System.out.println("Row add to table"); + } + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public void gameInfo(String gameName){ + PreparedStatement stmt = null; + try { + String query = "select * from games where title = ?"; + stmt = dataBase.getConnection().prepareStatement(query); + stmt.setString(1, gameName); + ResultSet resultSet = stmt.executeQuery(); + + JSONObject jsonObj = new JSONObject(); + resultSet.next(); + jsonObj.put("title", resultSet.getString(2)); + jsonObj.put("developer", resultSet.getString(3)); + jsonObj.put("genre", resultSet.getString(4)); + jsonObj.put("price", resultSet.getDouble(5)); + jsonObj.put("release_year", resultSet.getInt(6)); + jsonObj.put("controller_support", resultSet.getBoolean(7)); + jsonObj.put("reviews", resultSet.getInt(8)); + jsonObj.put("size", resultSet.getInt(9)); + + out.println(jsonObj); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public String hashPassword(String password) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] hash = md.digest(password.getBytes()); + StringBuilder sb = new StringBuilder(); + for (byte b : hash) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (NoSuchAlgorithmException e) { + // handle exception + return null; + } + } +}